mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 06:58:07 -05:00
feat(npm): added logic to disable auth, middleware validation, & db syncing for npm package
This commit is contained in:
@@ -1,92 +1,278 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Build Standalone Distribution
|
||||
* Build Standalone App Script
|
||||
*
|
||||
* This script builds a standalone distribution of Sim Studio that can be downloaded
|
||||
* and run by the CLI with `npx sim`.
|
||||
*
|
||||
* The standalone package includes:
|
||||
* - Pre-built Next.js static export
|
||||
* - Simplified Express server
|
||||
* - Configured to use localStorage instead of a database
|
||||
* This script builds a standalone version of Sim Studio that can be run without a database.
|
||||
* It creates a tarball that includes:
|
||||
* 1. A pre-built Next.js application
|
||||
* 2. A simple Express server to serve the application
|
||||
* 3. Configuration to use browser localStorage for data persistence
|
||||
*/
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { execSync } = require('child_process')
|
||||
const packageJson = require('../package.json')
|
||||
const tar = require('tar')
|
||||
const crypto = require('crypto')
|
||||
|
||||
// Configuration
|
||||
const STANDALONE_DIR = path.join(__dirname, '../standalone-dist')
|
||||
const SOURCE_DIR = path.join(__dirname, '..')
|
||||
const OUTPUT_TARBALL = path.join(__dirname, '../sim-standalone.tar.gz')
|
||||
const ROOT_DIR = path.resolve(__dirname, '..')
|
||||
const STANDALONE_DIR = path.join(ROOT_DIR, 'packages/@sim/cli/standalone')
|
||||
const STANDALONE_PACKAGE_JSON = path.join(STANDALONE_DIR, 'package.json')
|
||||
const STANDALONE_SERVER_JS = path.join(STANDALONE_DIR, 'server.js')
|
||||
const OUTPUT_TARBALL = path.join(ROOT_DIR, 'sim-standalone.tar.gz')
|
||||
|
||||
console.log('🔨 Building Sim Studio standalone distribution')
|
||||
|
||||
// Clean up if the directory exists
|
||||
if (fs.existsSync(STANDALONE_DIR)) {
|
||||
console.log('Cleaning up previous build...')
|
||||
fs.rmSync(STANDALONE_DIR, { recursive: true, force: true })
|
||||
// Ensure the standalone directory exists
|
||||
if (!fs.existsSync(STANDALONE_DIR)) {
|
||||
fs.mkdirSync(STANDALONE_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
// Create standalone directory
|
||||
// Clean the standalone directory first
|
||||
if (fs.existsSync(STANDALONE_DIR)) {
|
||||
fs.rmSync(STANDALONE_DIR, { recursive: true, force: true })
|
||||
}
|
||||
fs.mkdirSync(STANDALONE_DIR, { recursive: true })
|
||||
fs.mkdirSync(path.join(STANDALONE_DIR, 'public'), { recursive: true })
|
||||
|
||||
// Build Next.js static export
|
||||
console.log('Building Next.js static export...')
|
||||
// Build the Next.js app with local storage mode
|
||||
console.log('Building Next.js app in standalone mode...')
|
||||
try {
|
||||
// Set environment variable for static export with localStorage
|
||||
process.env.USE_LOCAL_STORAGE = 'true'
|
||||
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE = 'true'
|
||||
|
||||
// Build the app
|
||||
// Instead of using static export, we'll build a regular Next.js app
|
||||
// and then copy the necessary files to run it with a simple Express server
|
||||
execSync('npm run build', {
|
||||
cwd: SOURCE_DIR,
|
||||
cwd: ROOT_DIR,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
USE_LOCAL_STORAGE: 'true',
|
||||
NEXT_PUBLIC_USE_LOCAL_STORAGE: 'true',
|
||||
DISABLE_DB_SYNC: 'true',
|
||||
NODE_ENV: 'production',
|
||||
},
|
||||
})
|
||||
|
||||
// Copy the output to standalone directory
|
||||
console.log('Copying files to standalone directory...')
|
||||
fs.cpSync(path.join(SOURCE_DIR, 'out'), path.join(STANDALONE_DIR, 'public'), { recursive: true })
|
||||
} catch (error) {
|
||||
console.error('Error building Next.js static export:', error)
|
||||
console.error('Failed to build Next.js app:', error)
|
||||
console.log('Continuing with the standalone build process...')
|
||||
}
|
||||
|
||||
// Copy the built app to the standalone directory
|
||||
console.log('Copying built app to standalone directory...')
|
||||
try {
|
||||
// Copy the .next directory
|
||||
if (fs.existsSync(path.join(ROOT_DIR, '.next'))) {
|
||||
execSync(`cp -r ${path.join(ROOT_DIR, '.next')} ${path.join(STANDALONE_DIR, '.next')}`)
|
||||
}
|
||||
|
||||
// Copy the public directory
|
||||
if (fs.existsSync(path.join(ROOT_DIR, 'public'))) {
|
||||
execSync(`cp -r ${path.join(ROOT_DIR, 'public')} ${path.join(STANDALONE_DIR, 'public')}`)
|
||||
}
|
||||
|
||||
// Copy necessary files for standalone operation
|
||||
if (fs.existsSync(path.join(ROOT_DIR, 'next.config.ts'))) {
|
||||
execSync(
|
||||
`cp ${path.join(ROOT_DIR, 'next.config.ts')} ${path.join(STANDALONE_DIR, 'next.config.ts')}`
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to copy built app:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Copy standalone server files
|
||||
console.log('Copying standalone server files...')
|
||||
fs.copyFileSync(
|
||||
path.join(SOURCE_DIR, 'packages/@sim/cli/standalone/server.js'),
|
||||
path.join(STANDALONE_DIR, 'server.js')
|
||||
)
|
||||
fs.copyFileSync(
|
||||
path.join(SOURCE_DIR, 'packages/@sim/cli/standalone/package.json'),
|
||||
path.join(STANDALONE_DIR, 'package.json')
|
||||
)
|
||||
// Create a package.json for the standalone app
|
||||
console.log('Creating package.json for standalone app...')
|
||||
const packageJson = {
|
||||
name: 'sim-studio-standalone',
|
||||
version: '0.1.0',
|
||||
private: true,
|
||||
scripts: {
|
||||
start: 'node server.js',
|
||||
},
|
||||
dependencies: {
|
||||
express: '^4.18.2',
|
||||
next: '^15.2.0',
|
||||
react: '^18.2.0',
|
||||
'react-dom': '^18.2.0',
|
||||
compression: '^1.7.4',
|
||||
'serve-favicon': '^2.5.0',
|
||||
typescript: '^5.7.3',
|
||||
},
|
||||
}
|
||||
|
||||
// Create tarball
|
||||
fs.writeFileSync(STANDALONE_PACKAGE_JSON, JSON.stringify(packageJson, null, 2))
|
||||
|
||||
// Create a simple Express server to serve the static files
|
||||
console.log('Creating server.js for standalone app...')
|
||||
const serverJs = `
|
||||
const express = require('express');
|
||||
const next = require('next');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const compression = require('compression');
|
||||
const favicon = require('serve-favicon');
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Register TypeScript compiler
|
||||
require('typescript');
|
||||
|
||||
// Set environment variables for standalone mode
|
||||
process.env.USE_LOCAL_STORAGE = 'true';
|
||||
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE = 'true';
|
||||
process.env.DISABLE_DB_SYNC = 'true';
|
||||
process.env.DISABLE_AUTH = 'true';
|
||||
process.env.NEXT_PUBLIC_DISABLE_AUTH = 'true';
|
||||
|
||||
const port = process.env.SIM_STUDIO_PORT || 3000;
|
||||
const dev = false; // Always run in production mode
|
||||
|
||||
// Initialize Next.js
|
||||
const app = next({ dev, dir: __dirname });
|
||||
const handle = app.getRequestHandler();
|
||||
|
||||
app.prepare().then(() => {
|
||||
const server = express();
|
||||
|
||||
// Enable compression
|
||||
server.use(compression());
|
||||
|
||||
// Serve favicon
|
||||
if (fs.existsSync(path.join(__dirname, 'public', 'favicon.ico'))) {
|
||||
server.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
|
||||
}
|
||||
|
||||
// Middleware to set environment variables for each request
|
||||
server.use((req, res, next) => {
|
||||
// These will be available to the API routes
|
||||
process.env.USE_LOCAL_STORAGE = 'true';
|
||||
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE = 'true';
|
||||
process.env.DISABLE_DB_SYNC = 'true';
|
||||
process.env.DISABLE_AUTH = 'true';
|
||||
process.env.NEXT_PUBLIC_DISABLE_AUTH = 'true';
|
||||
next();
|
||||
});
|
||||
|
||||
// Redirect root path to a new workflow
|
||||
server.get('/', (req, res) => {
|
||||
// Generate a UUID for the new workflow
|
||||
const uuid = crypto.randomUUID();
|
||||
console.log(\`Redirecting to new workflow: \${uuid}\`);
|
||||
res.redirect(\`/w/\${uuid}\`);
|
||||
});
|
||||
|
||||
// Handle all other requests with Next.js
|
||||
server.all('*', (req, res) => {
|
||||
return handle(req, res);
|
||||
});
|
||||
|
||||
// Start the server
|
||||
server.listen(port, (err) => {
|
||||
if (err) throw err;
|
||||
console.log(\`> Sim Studio standalone server ready on http://localhost:\${port}\`);
|
||||
console.log('> Running in local storage mode - all data will be stored in the browser');
|
||||
console.log('> Authentication is disabled - anyone can access the app');
|
||||
});
|
||||
});
|
||||
`
|
||||
|
||||
fs.writeFileSync(STANDALONE_SERVER_JS, serverJs)
|
||||
|
||||
// Skip creating .env file for the standalone app
|
||||
console.log('Skipping .env file creation for standalone app...')
|
||||
|
||||
// Create a tsconfig.json for the standalone app
|
||||
console.log('Creating tsconfig.json for standalone app...')
|
||||
const tsconfigJson = {
|
||||
compilerOptions: {
|
||||
target: 'es5',
|
||||
lib: ['dom', 'dom.iterable', 'esnext'],
|
||||
allowJs: true,
|
||||
skipLibCheck: true,
|
||||
strict: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
noEmit: true,
|
||||
esModuleInterop: true,
|
||||
module: 'esnext',
|
||||
moduleResolution: 'node',
|
||||
resolveJsonModule: true,
|
||||
isolatedModules: true,
|
||||
jsx: 'preserve',
|
||||
incremental: true,
|
||||
plugins: [
|
||||
{
|
||||
name: 'next',
|
||||
},
|
||||
],
|
||||
paths: {
|
||||
'@/*': ['./*'],
|
||||
},
|
||||
},
|
||||
include: ['next-env.d.ts', '**/*.ts', '**/*.tsx', '.next/types/**/*.ts'],
|
||||
exclude: ['node_modules'],
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(STANDALONE_DIR, 'tsconfig.json'), JSON.stringify(tsconfigJson, null, 2))
|
||||
|
||||
// Create a README.md for the standalone app
|
||||
console.log('Creating README.md for standalone app...')
|
||||
const readmeFile = `# Sim Studio Standalone
|
||||
|
||||
This is a standalone version of Sim Studio that runs without a database. All data is stored in your browser's localStorage.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
\`\`\`
|
||||
npm install
|
||||
\`\`\`
|
||||
|
||||
2. Start the server:
|
||||
\`\`\`
|
||||
npm start
|
||||
\`\`\`
|
||||
|
||||
3. Open your browser to http://localhost:3000
|
||||
|
||||
## Features
|
||||
|
||||
- **Full Functionality**: All features of Sim Studio are available, including API routes
|
||||
- **No Authentication Required**: Just start building workflows right away
|
||||
- **Local Storage**: All your workflows and settings are stored in your browser's localStorage
|
||||
- **Drag and Drop Interface**: Easily create and connect workflow blocks
|
||||
- **Real-time Execution**: Test your workflows as you build them
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can customize the app by setting these environment variables:
|
||||
|
||||
- \`SIM_STUDIO_PORT\`: Change the port (default: 3000)
|
||||
|
||||
## How It Works
|
||||
|
||||
When you start the app, it will:
|
||||
1. Automatically redirect you to a new workflow with a unique ID
|
||||
2. Allow you to drag and drop blocks to build your workflow
|
||||
3. Save all your work to localStorage in your browser
|
||||
4. Let you execute workflows directly in the browser
|
||||
|
||||
No database or authentication is required!
|
||||
`
|
||||
|
||||
fs.writeFileSync(path.join(STANDALONE_DIR, 'README.md'), readmeFile)
|
||||
|
||||
// Create the tarball
|
||||
console.log('Creating tarball...')
|
||||
try {
|
||||
execSync(`tar -czf "${OUTPUT_TARBALL}" -C "${STANDALONE_DIR}" .`, {
|
||||
stdio: 'inherit',
|
||||
})
|
||||
// Remove any .env files before creating the tarball
|
||||
console.log('Removing any .env files from the standalone directory...')
|
||||
execSync('find ' + STANDALONE_DIR + ' -name ".env*" -type f -delete')
|
||||
|
||||
console.log(`✅ Standalone distribution created: ${OUTPUT_TARBALL}`)
|
||||
console.log(`📦 Size: ${(fs.statSync(OUTPUT_TARBALL).size / (1024 * 1024)).toFixed(2)} MB`)
|
||||
// Create the tarball
|
||||
execSync(
|
||||
`tar -czf ${OUTPUT_TARBALL} -C ${path.dirname(STANDALONE_DIR)} ${path.basename(STANDALONE_DIR)}`
|
||||
)
|
||||
console.log(`Standalone app built and packaged to ${OUTPUT_TARBALL}`)
|
||||
console.log('You can now upload this file to GitHub releases')
|
||||
} catch (error) {
|
||||
console.error('Error creating tarball:', error)
|
||||
console.error('Failed to create tarball:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('\n🚀 Next steps:')
|
||||
console.log('1. Upload the tarball to your release assets')
|
||||
console.log('2. Update the DOWNLOAD_URL in the CLI code to point to your release')
|
||||
console.log('3. Publish the CLI package to npm with: npm run cli:publish')
|
||||
|
||||
102
scripts/release-npm.js
Executable file
102
scripts/release-npm.js
Executable file
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Release NPM Package Script
|
||||
*
|
||||
* This script helps with the process of releasing the Sim Studio CLI to npm.
|
||||
* It builds the standalone app, prepares the CLI package, and publishes it to npm.
|
||||
*/
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { execSync } = require('child_process')
|
||||
|
||||
// Configuration
|
||||
const ROOT_DIR = path.resolve(__dirname, '..')
|
||||
const CLI_DIR = path.join(ROOT_DIR, 'packages/@sim/cli')
|
||||
const STANDALONE_DIR = path.join(CLI_DIR, 'standalone')
|
||||
const OUTPUT_TARBALL = path.join(ROOT_DIR, 'sim-standalone.tar.gz')
|
||||
|
||||
// Ensure we're in the right directory
|
||||
process.chdir(ROOT_DIR)
|
||||
|
||||
// Helper function to run commands and log output
|
||||
function runCommand(command, options = {}) {
|
||||
console.log(`> ${command}`)
|
||||
try {
|
||||
execSync(command, {
|
||||
stdio: 'inherit',
|
||||
...options,
|
||||
})
|
||||
return true
|
||||
} catch (error) {
|
||||
console.error(`Error running command: ${command}`)
|
||||
console.error(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Main release process
|
||||
async function release() {
|
||||
console.log('=== Sim Studio CLI Release Process ===')
|
||||
|
||||
// 1. Clean up any existing standalone files
|
||||
console.log('\n1. Cleaning up existing standalone files...')
|
||||
if (fs.existsSync(STANDALONE_DIR)) {
|
||||
fs.rmSync(STANDALONE_DIR, { recursive: true, force: true })
|
||||
}
|
||||
if (fs.existsSync(OUTPUT_TARBALL)) {
|
||||
fs.unlinkSync(OUTPUT_TARBALL)
|
||||
}
|
||||
|
||||
// 2. Build the standalone app
|
||||
console.log('\n2. Building standalone app...')
|
||||
if (!runCommand('node scripts/build-standalone.js')) {
|
||||
console.error('Failed to build standalone app')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// 3. Prepare the CLI package
|
||||
console.log('\n3. Preparing CLI package...')
|
||||
process.chdir(CLI_DIR)
|
||||
|
||||
// Clean and build
|
||||
if (!runCommand('npm run clean && npm run build')) {
|
||||
console.error('Failed to build CLI package')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// 4. Create the standalone directory
|
||||
console.log('\n4. Creating standalone directory...')
|
||||
if (!fs.existsSync(STANDALONE_DIR)) {
|
||||
fs.mkdirSync(STANDALONE_DIR, { recursive: true })
|
||||
}
|
||||
|
||||
// 5. Publish to npm
|
||||
console.log('\n5. Publishing to npm...')
|
||||
console.log('Ready to publish to npm. Run the following commands:')
|
||||
console.log(`
|
||||
cd ${path.relative(process.cwd(), CLI_DIR)}
|
||||
npm publish
|
||||
`)
|
||||
|
||||
// 6. Create GitHub release
|
||||
console.log('\n6. Creating GitHub release...')
|
||||
console.log('After publishing to npm, create a GitHub release with the standalone tarball:')
|
||||
console.log(`
|
||||
1. Go to https://github.com/simstudioai/sim/releases/new
|
||||
2. Set the tag to v0.1.0 (or your current version)
|
||||
3. Set the title to "Sim Studio v0.1.0"
|
||||
4. Upload the tarball: ${path.relative(ROOT_DIR, OUTPUT_TARBALL)}
|
||||
5. Publish the release
|
||||
`)
|
||||
|
||||
console.log('\n=== Release Process Complete ===')
|
||||
console.log('Follow the instructions above to publish to npm and create a GitHub release.')
|
||||
}
|
||||
|
||||
// Run the release process
|
||||
release().catch((error) => {
|
||||
console.error('Release process failed:', error)
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in New Issue
Block a user