mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 23:17:59 -05:00
feat(npm): added logic to disable auth, middleware validation, & db syncing for npm package
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -32,6 +32,12 @@ yarn-error.log*
|
|||||||
|
|
||||||
# env files
|
# env files
|
||||||
.env
|
.env
|
||||||
|
.env.*
|
||||||
|
*.env
|
||||||
|
.env.local
|
||||||
|
.env.development
|
||||||
|
.env.test
|
||||||
|
.env.production
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|||||||
88
RELEASE.md
Normal file
88
RELEASE.md
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
# Releasing Sim Studio to npm
|
||||||
|
|
||||||
|
This guide outlines the steps to release Sim Studio CLI to npm and create a GitHub release with the standalone app.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Node.js 16 or higher
|
||||||
|
- npm account with access to the `simstudio` package
|
||||||
|
- GitHub access to the `simstudioai/sim` repository
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
### 1. Prepare the Release
|
||||||
|
|
||||||
|
1. Ensure all changes are committed and pushed to the main branch
|
||||||
|
2. Update the version number in `packages/@sim/cli/package.json`
|
||||||
|
3. Update the `STANDALONE_VERSION` in `packages/@sim/cli/src/commands/start.ts` to match
|
||||||
|
|
||||||
|
### 2. Run the Release Script
|
||||||
|
|
||||||
|
The release script automates most of the process:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node scripts/release-npm.js
|
||||||
|
```
|
||||||
|
|
||||||
|
This script will:
|
||||||
|
|
||||||
|
- Clean up any existing standalone files
|
||||||
|
- Build the standalone app
|
||||||
|
- Prepare the CLI package
|
||||||
|
- Create the standalone directory
|
||||||
|
- Provide instructions for publishing to npm and creating a GitHub release
|
||||||
|
|
||||||
|
### 3. Publish to npm
|
||||||
|
|
||||||
|
After the script completes, follow the instructions to publish to npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd packages/@sim/cli
|
||||||
|
npm publish
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Create GitHub Release
|
||||||
|
|
||||||
|
1. Go to https://github.com/simstudioai/sim/releases/new
|
||||||
|
2. Set the tag to match your version (e.g., `v0.1.0`)
|
||||||
|
3. Set the title to "Sim Studio v0.1.0" (replace with your version)
|
||||||
|
4. Upload the `sim-standalone.tar.gz` file from the project root
|
||||||
|
5. Add release notes describing the changes
|
||||||
|
6. Publish the release
|
||||||
|
|
||||||
|
## Testing the Release
|
||||||
|
|
||||||
|
To test the released package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install globally
|
||||||
|
npm install -g simstudio
|
||||||
|
|
||||||
|
# Run the CLI
|
||||||
|
simstudio start
|
||||||
|
|
||||||
|
# Or run with npx
|
||||||
|
npx simstudio start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### npm publish fails
|
||||||
|
|
||||||
|
- Ensure you're logged in to npm: `npm login`
|
||||||
|
- Check that the package name is available: `npm view simstudio`
|
||||||
|
- Verify the version number is higher than the previously published version
|
||||||
|
|
||||||
|
### GitHub release fails
|
||||||
|
|
||||||
|
- Ensure you have the correct permissions to create releases
|
||||||
|
- Check that the tag doesn't already exist
|
||||||
|
- Verify the tarball was created correctly
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
After a successful release:
|
||||||
|
|
||||||
|
1. Increment the version number in `packages/@sim/cli/package.json` for the next release
|
||||||
|
2. Update the `STANDALONE_VERSION` in `packages/@sim/cli/src/commands/start.ts`
|
||||||
|
3. Commit these changes with a message like "Bump version to X.Y.Z"
|
||||||
@@ -22,6 +22,51 @@ export function useWorkflowExecution() {
|
|||||||
const [executionResult, setExecutionResult] = useState<ExecutionResult | null>(null)
|
const [executionResult, setExecutionResult] = useState<ExecutionResult | null>(null)
|
||||||
|
|
||||||
const persistLogs = async (logs: any[], executionId: string) => {
|
const persistLogs = async (logs: any[], executionId: string) => {
|
||||||
|
// Check if we're in local storage mode
|
||||||
|
const useLocalStorage =
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(window.localStorage.getItem('USE_LOCAL_STORAGE') === 'true' ||
|
||||||
|
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE === 'true')
|
||||||
|
|
||||||
|
if (useLocalStorage) {
|
||||||
|
// Store logs in localStorage
|
||||||
|
try {
|
||||||
|
const storageKey = `workflow-logs-${activeWorkflowId}-${executionId}`
|
||||||
|
window.localStorage.setItem(
|
||||||
|
storageKey,
|
||||||
|
JSON.stringify({
|
||||||
|
logs,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
workflowId: activeWorkflowId,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Also update a list of all execution logs for this workflow
|
||||||
|
const logListKey = `workflow-logs-list-${activeWorkflowId}`
|
||||||
|
const existingLogList = window.localStorage.getItem(logListKey)
|
||||||
|
const logList = existingLogList ? JSON.parse(existingLogList) : []
|
||||||
|
logList.push({
|
||||||
|
executionId,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Keep only the last 20 executions
|
||||||
|
if (logList.length > 20) {
|
||||||
|
const removedLogs = logList.splice(0, logList.length - 20)
|
||||||
|
// Clean up old logs
|
||||||
|
removedLogs.forEach((log: any) => {
|
||||||
|
window.localStorage.removeItem(`workflow-logs-${activeWorkflowId}-${log.executionId}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
window.localStorage.setItem(logListKey, JSON.stringify(logList))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error storing logs in localStorage:', error)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to API if not in local storage mode
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/workflow/${activeWorkflowId}/log`, {
|
const response = await fetch(`/api/workflow/${activeWorkflowId}/log`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ export async function middleware(request: NextRequest) {
|
|||||||
return NextResponse.redirect(new URL('/w/1', request.url))
|
return NextResponse.redirect(new URL('/w/1', request.url))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip auth check if DISABLE_AUTH is set (for standalone mode)
|
||||||
|
if (process.env.DISABLE_AUTH === 'true' || process.env.NEXT_PUBLIC_DISABLE_AUTH === 'true') {
|
||||||
|
return NextResponse.next()
|
||||||
|
}
|
||||||
|
|
||||||
// Existing auth check for protected routes
|
// Existing auth check for protected routes
|
||||||
const sessionCookie = getSessionCookie(request)
|
const sessionCookie = getSessionCookie(request)
|
||||||
if (!sessionCookie) {
|
if (!sessionCookie) {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ const nextConfig: NextConfig = {
|
|||||||
// Enable static image optimization for standalone export
|
// Enable static image optimization for standalone export
|
||||||
unoptimized: isStandaloneBuild,
|
unoptimized: isStandaloneBuild,
|
||||||
},
|
},
|
||||||
// Use 'export' for standalone builds, 'standalone' for regular builds
|
// Always use 'standalone' output to support API routes
|
||||||
output: isStandaloneBuild ? 'export' : 'standalone',
|
output: 'standalone',
|
||||||
// Only include headers when not building for standalone export
|
// Only include headers when not building for standalone export
|
||||||
...(isStandaloneBuild
|
...(isStandaloneBuild
|
||||||
? {}
|
? {}
|
||||||
|
|||||||
@@ -15,12 +15,13 @@
|
|||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"cli:build": "npm run build -w @sim/cli",
|
"cli:build": "npm run build -w packages/@sim/cli",
|
||||||
"cli:dev": "npm run build -w @sim/cli && cd packages/@sim/cli && node ./dist/index.js",
|
"cli:dev": "npm run build -w packages/@sim/cli && cd packages/@sim/cli && node ./dist/index.js",
|
||||||
"cli:publish": "cd packages/@sim/cli && npm publish --access public",
|
"cli:publish": "cd packages/@sim/cli && npm publish --access public",
|
||||||
"cli:start": "cd packages/@sim/cli && node ./dist/index.js start",
|
"cli:start": "cd packages/@sim/cli && node ./dist/index.js start",
|
||||||
"build:standalone": "node scripts/build-standalone.js",
|
"build:standalone": "node scripts/build-standalone.js",
|
||||||
"build:cli": "npm run cli:build && npm run build:standalone"
|
"build:cli": "npm run cli:build && npm run build:standalone",
|
||||||
|
"publish:cli": "npm run build:cli && npm run cli:publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/sdk": "^0.38.0",
|
"@anthropic-ai/sdk": "^0.38.0",
|
||||||
|
|||||||
34
packages/@sim/cli/.npmignore
Normal file
34
packages/@sim/cli/.npmignore
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# Environment variables and secrets
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
*.env
|
||||||
|
.env.local
|
||||||
|
.env.development
|
||||||
|
.env.test
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# Development files
|
||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.github
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Test files
|
||||||
|
test
|
||||||
|
tests
|
||||||
|
__tests__
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
.DS_Store
|
||||||
|
.cache
|
||||||
|
.next/cache
|
||||||
|
|
||||||
|
# Project specific
|
||||||
|
.sim-studio-dev
|
||||||
@@ -1,101 +1,79 @@
|
|||||||
# Sim Studio CLI
|
# Sim Studio CLI
|
||||||
|
|
||||||
The Sim Studio CLI provides a convenient way to run Sim Studio directly from your terminal without needing to set up a database or complex environment.
|
A command-line interface for Sim Studio - a powerful, user-friendly platform for building, testing, and optimizing agentic workflows.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g simstudio
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run directly with npx:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx simstudio
|
||||||
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
The fastest way to get started is to run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run Sim Studio with default settings
|
npx simstudio start
|
||||||
npx sim
|
```
|
||||||
|
|
||||||
# Start with custom port
|
This will download and start a standalone version of Sim Studio, with all data stored in your browser's localStorage. No database or authentication required!
|
||||||
npx sim start -p 8080
|
|
||||||
|
|
||||||
# Get help
|
## Usage
|
||||||
npx sim help
|
|
||||||
|
### Start Sim Studio
|
||||||
|
|
||||||
|
Start a local instance of Sim Studio:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simstudio start
|
||||||
|
```
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
- `--port <port>` - Specify the port to run on (default: 3000)
|
||||||
|
- `--debug` - Run in debug mode
|
||||||
|
|
||||||
|
### Help
|
||||||
|
|
||||||
|
Get help with available commands:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
simstudio --help
|
||||||
```
|
```
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Zero Configuration**: Get started immediately with `npx sim`
|
- **Local Storage Mode**: All your workflows and settings are stored in your browser's localStorage, no database required
|
||||||
- **Local Storage**: Works entirely in the browser, no database required
|
- **Workflow Builder**: Create and edit workflows with a visual editor
|
||||||
- **Persistence**: Your workflows and data persist between sessions
|
- **Workflow Execution**: Run workflows and see the results in real-time
|
||||||
- **Familiar Experience**: All the power of Sim Studio in a simplified package
|
- **Environment Variables**: Manage environment variables for your workflows
|
||||||
|
|
||||||
## Commands
|
## How It Works
|
||||||
|
|
||||||
- `sim` - Start Sim Studio with default settings
|
When you run `simstudio start`, the CLI will:
|
||||||
- `sim start` - Start Sim Studio with options
|
|
||||||
- `sim version` - Display version information
|
|
||||||
- `sim help` - Show help and usage information
|
|
||||||
|
|
||||||
## Options
|
1. Check if you're in a Sim Studio project directory
|
||||||
|
2. If not, download and extract a standalone version of Sim Studio
|
||||||
|
3. Start a local server with the standalone app
|
||||||
|
4. Open a browser window to the Sim Studio UI
|
||||||
|
|
||||||
- `-p, --port <port>` - Specify port (default: 3000)
|
All your data is stored in your browser's localStorage, so you can close the app and come back later without losing your work.
|
||||||
- `-d, --debug` - Enable debug mode
|
|
||||||
- `-v, --version` - Show version information
|
|
||||||
- `-h, --help` - Show help information
|
|
||||||
|
|
||||||
## Local Storage Mode
|
## Development
|
||||||
|
|
||||||
When running Sim Studio via the CLI, all data is stored using the browser's localStorage. This means:
|
To contribute to the development of Sim Studio CLI:
|
||||||
|
|
||||||
- Your workflows persist between browser sessions
|
1. Clone the repository
|
||||||
- No database configuration is required
|
2. Install dependencies with `npm install`
|
||||||
- Data is stored locally on your device
|
3. Build the CLI with `npm run build`
|
||||||
- Multiple users can't share the same workflows (single-user mode)
|
4. Link the CLI for local development with `npm link`
|
||||||
|
|
||||||
## Advanced Usage
|
## License
|
||||||
|
|
||||||
If you need multi-user capabilities or want to store data in a database, consider:
|
MIT
|
||||||
|
|
||||||
1. Using the Docker setup in the main repository
|
|
||||||
2. Setting up a full Sim Studio environment with PostgreSQL
|
|
||||||
3. Deploying to Vercel with a database
|
|
||||||
|
|
||||||
## For Developers: Building & Publishing the CLI
|
|
||||||
|
|
||||||
### Release Checklist
|
|
||||||
|
|
||||||
1. ✅ Update the CLI code with your changes
|
|
||||||
2. ✅ Bump the version in `package.json`
|
|
||||||
3. ✅ Build the standalone version:
|
|
||||||
```
|
|
||||||
npm run build:cli
|
|
||||||
```
|
|
||||||
4. ✅ Upload the generated `sim-standalone.tar.gz` to GitHub releases
|
|
||||||
5. ✅ Update the `DOWNLOAD_URL` constant in `packages/@sim/cli/src/commands/start.ts` to point to the new release URL
|
|
||||||
6. ✅ Commit all changes
|
|
||||||
7. ✅ Publish to npm:
|
|
||||||
```
|
|
||||||
npm run cli:publish
|
|
||||||
```
|
|
||||||
|
|
||||||
### About the Standalone Version
|
|
||||||
|
|
||||||
The standalone version is a pre-built and bundled version of Sim Studio that can run without a database or complex setup. It includes:
|
|
||||||
|
|
||||||
- A pre-built static export of the Next.js application
|
|
||||||
- A simple Express server to serve the static files
|
|
||||||
- Configuration to use browser localStorage for data persistence
|
|
||||||
|
|
||||||
This allows users to quickly try Sim Studio with a simple `npx sim` command without installing anything else.
|
|
||||||
|
|
||||||
### Testing the CLI Locally
|
|
||||||
|
|
||||||
To test the CLI locally:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build the CLI
|
|
||||||
npm run cli:build
|
|
||||||
|
|
||||||
# Run the CLI directly
|
|
||||||
npm run cli:start
|
|
||||||
|
|
||||||
# Or use the dev script
|
|
||||||
npm run cli:dev
|
|
||||||
```
|
|
||||||
|
|
||||||
## Need Help?
|
|
||||||
|
|
||||||
Visit our [documentation](https://github.com/yourusername/sim) or open an issue on GitHub.
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// This file is the entry point for the 'sim' command
|
// This file is the entry point for the 'simstudio' command
|
||||||
try {
|
try {
|
||||||
require('../dist/index.js')
|
require('../dist/index.js')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'MODULE_NOT_FOUND') {
|
if (error.code === 'MODULE_NOT_FOUND') {
|
||||||
console.error('Sim CLI has not been built. Please run npm run build first.')
|
console.error('Sim Studio CLI has not been built. Please run npm run build first.')
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
} else {
|
} else {
|
||||||
console.error('An error occurred while starting Sim CLI:', error)
|
console.error('An error occurred while starting Sim Studio CLI:', error)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
packages/@sim/cli/dist/commands/start.js
vendored
45
packages/@sim/cli/dist/commands/start.js
vendored
@@ -19,7 +19,7 @@ const spinner_1 = require("../utils/spinner");
|
|||||||
const SIM_HOME_DIR = path_1.default.join(os_1.default.homedir(), '.sim-studio');
|
const SIM_HOME_DIR = path_1.default.join(os_1.default.homedir(), '.sim-studio');
|
||||||
const SIM_STANDALONE_DIR = path_1.default.join(SIM_HOME_DIR, 'standalone');
|
const SIM_STANDALONE_DIR = path_1.default.join(SIM_HOME_DIR, 'standalone');
|
||||||
const SIM_VERSION_FILE = path_1.default.join(SIM_HOME_DIR, 'version.json');
|
const SIM_VERSION_FILE = path_1.default.join(SIM_HOME_DIR, 'version.json');
|
||||||
const DOWNLOAD_URL = 'https://github.com/simstudioai/sim/releases/download/v0.1.0/sim-standalone.tar.gz';
|
const DOWNLOAD_URL = 'https://github.com/simstudioai/sim/releases/latest/download/sim-standalone.tar.gz';
|
||||||
const STANDALONE_VERSION = '0.1.0';
|
const STANDALONE_VERSION = '0.1.0';
|
||||||
/**
|
/**
|
||||||
* Start command that launches Sim Studio using local storage
|
* Start command that launches Sim Studio using local storage
|
||||||
@@ -39,6 +39,8 @@ async function start(options) {
|
|||||||
...process.env,
|
...process.env,
|
||||||
PORT: port,
|
PORT: port,
|
||||||
USE_LOCAL_STORAGE: 'true', // Key environment variable to switch to local storage
|
USE_LOCAL_STORAGE: 'true', // Key environment variable to switch to local storage
|
||||||
|
NEXT_PUBLIC_USE_LOCAL_STORAGE: 'true', // For client-side code
|
||||||
|
DISABLE_DB_SYNC: 'true', // Disable database sync
|
||||||
NODE_ENV: debug ? 'development' : 'production',
|
NODE_ENV: debug ? 'development' : 'production',
|
||||||
DEBUG: debug ? '*' : undefined,
|
DEBUG: debug ? '*' : undefined,
|
||||||
};
|
};
|
||||||
@@ -50,11 +52,40 @@ async function start(options) {
|
|||||||
// Running from within the project directory - we'll use the existing
|
// Running from within the project directory - we'll use the existing
|
||||||
// Next.js setup directly
|
// Next.js setup directly
|
||||||
spinner.text = 'Detected Sim Studio project, starting with local configuration...';
|
spinner.text = 'Detected Sim Studio project, starting with local configuration...';
|
||||||
simProcess = (0, child_process_1.spawn)('npm', ['run', 'dev'], {
|
// When running in dev mode, we need to make sure we're not trying to use static export
|
||||||
env,
|
// as it will fail with API routes
|
||||||
stdio: 'inherit',
|
if (debug) {
|
||||||
shell: true,
|
spinner.text = 'Starting in development mode with local storage...';
|
||||||
});
|
simProcess = (0, child_process_1.spawn)('npm', ['run', 'dev'], {
|
||||||
|
env: env,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// In production mode, we'll use the start command which uses the built app
|
||||||
|
spinner.text = 'Starting in production mode with local storage...';
|
||||||
|
// Build first if needed
|
||||||
|
if (!fs_1.default.existsSync(path_1.default.join(process.cwd(), '.next'))) {
|
||||||
|
spinner.text = 'Building Next.js app first...';
|
||||||
|
try {
|
||||||
|
(0, child_process_2.execSync)('npm run build', {
|
||||||
|
env: env,
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
spinner.fail('Failed to build Next.js app');
|
||||||
|
console.error(chalk_1.default.red('Error:'), error instanceof Error ? error.message : error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
simProcess = (0, child_process_1.spawn)('npm', ['run', 'start'], {
|
||||||
|
env: env,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Running from outside the project via npx - we'll download and start a standalone version
|
// Running from outside the project via npx - we'll download and start a standalone version
|
||||||
@@ -153,7 +184,7 @@ function checkIfInProjectDirectory() {
|
|||||||
if (packageJson.name === 'sim' ||
|
if (packageJson.name === 'sim' ||
|
||||||
packageJson.name === 'sim-studio' ||
|
packageJson.name === 'sim-studio' ||
|
||||||
(packageJson.dependencies &&
|
(packageJson.dependencies &&
|
||||||
(packageJson.dependencies['next'] || packageJson.dependencies['@sim/cli']))) {
|
(packageJson.dependencies['next'] || packageJson.dependencies['@sim/cli'] || packageJson.dependencies['sim-studio-cli']))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@sim/cli",
|
"name": "simstudio",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "CLI tool for Sim Studio - easily start, build and test agent workflows",
|
"description": "CLI tool for Sim Studio - easily start, build and test agent workflows",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -7,19 +7,21 @@
|
|||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"type": "commonjs",
|
"type": "commonjs",
|
||||||
"bin": {
|
"bin": {
|
||||||
"sim": "./bin/sim.js"
|
"simstudio": "./bin/sim.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"bin",
|
"bin",
|
||||||
"dist",
|
"dist",
|
||||||
"README.md"
|
"README.md",
|
||||||
|
"standalone"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"start": "node bin/sim.js",
|
"start": "node bin/sim.js",
|
||||||
"dev": "ts-node src/index.ts",
|
"dev": "ts-node src/index.ts",
|
||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"prepublishOnly": "npm run clean && npm run build"
|
"prepublishOnly": "npm run clean && npm run build && npm run prepare-standalone && echo 'Checking for sensitive files...' && (! find . -name '.env*' -not -path '*/node_modules/*' -not -path '*/standalone/*' | grep -q .)",
|
||||||
|
"prepare-standalone": "node ../../../scripts/build-standalone.js"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"sim",
|
"sim",
|
||||||
@@ -27,7 +29,9 @@
|
|||||||
"workflow",
|
"workflow",
|
||||||
"automation",
|
"automation",
|
||||||
"cli",
|
"cli",
|
||||||
"agent"
|
"agent",
|
||||||
|
"ai",
|
||||||
|
"workflow-automation"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
@@ -49,5 +53,16 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.0.0"
|
"node": ">=16.0.0"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/simstudioai/sim"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/simstudioai/sim/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/simstudioai/sim#readme",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const SIM_HOME_DIR = path.join(os.homedir(), '.sim-studio')
|
|||||||
const SIM_STANDALONE_DIR = path.join(SIM_HOME_DIR, 'standalone')
|
const SIM_STANDALONE_DIR = path.join(SIM_HOME_DIR, 'standalone')
|
||||||
const SIM_VERSION_FILE = path.join(SIM_HOME_DIR, 'version.json')
|
const SIM_VERSION_FILE = path.join(SIM_HOME_DIR, 'version.json')
|
||||||
const DOWNLOAD_URL =
|
const DOWNLOAD_URL =
|
||||||
'https://github.com/simstudioai/sim/releases/download/v0.1.0/sim-standalone.tar.gz'
|
'https://github.com/simstudioai/sim/releases/latest/download/sim-standalone.tar.gz'
|
||||||
const STANDALONE_VERSION = '0.1.0'
|
const STANDALONE_VERSION = '0.1.0'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -44,6 +44,8 @@ export async function start(options: StartOptions) {
|
|||||||
...process.env,
|
...process.env,
|
||||||
PORT: port,
|
PORT: port,
|
||||||
USE_LOCAL_STORAGE: 'true', // Key environment variable to switch to local storage
|
USE_LOCAL_STORAGE: 'true', // Key environment variable to switch to local storage
|
||||||
|
NEXT_PUBLIC_USE_LOCAL_STORAGE: 'true', // For client-side code
|
||||||
|
DISABLE_DB_SYNC: 'true', // Disable database sync
|
||||||
NODE_ENV: debug ? 'development' : ('production' as const),
|
NODE_ENV: debug ? 'development' : ('production' as const),
|
||||||
DEBUG: debug ? '*' : undefined,
|
DEBUG: debug ? '*' : undefined,
|
||||||
}
|
}
|
||||||
@@ -59,11 +61,40 @@ export async function start(options: StartOptions) {
|
|||||||
// Next.js setup directly
|
// Next.js setup directly
|
||||||
spinner.text = 'Detected Sim Studio project, starting with local configuration...'
|
spinner.text = 'Detected Sim Studio project, starting with local configuration...'
|
||||||
|
|
||||||
simProcess = spawn('npm', ['run', 'dev'], {
|
// When running in dev mode, we need to make sure we're not trying to use static export
|
||||||
env: env as NodeJS.ProcessEnv,
|
// as it will fail with API routes
|
||||||
stdio: 'inherit',
|
if (debug) {
|
||||||
shell: true,
|
spinner.text = 'Starting in development mode with local storage...'
|
||||||
})
|
simProcess = spawn('npm', ['run', 'dev'], {
|
||||||
|
env: env as NodeJS.ProcessEnv,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// In production mode, we'll use the start command which uses the built app
|
||||||
|
spinner.text = 'Starting in production mode with local storage...'
|
||||||
|
|
||||||
|
// Build first if needed
|
||||||
|
if (!fs.existsSync(path.join(process.cwd(), '.next'))) {
|
||||||
|
spinner.text = 'Building Next.js app first...'
|
||||||
|
try {
|
||||||
|
execSync('npm run build', {
|
||||||
|
env: env as NodeJS.ProcessEnv,
|
||||||
|
stdio: 'inherit',
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
spinner.fail('Failed to build Next.js app')
|
||||||
|
console.error(chalk.red('Error:'), error instanceof Error ? error.message : error)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simProcess = spawn('npm', ['run', 'start'], {
|
||||||
|
env: env as NodeJS.ProcessEnv,
|
||||||
|
stdio: 'inherit',
|
||||||
|
shell: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Running from outside the project via npx - we'll download and start a standalone version
|
// Running from outside the project via npx - we'll download and start a standalone version
|
||||||
spinner.text = 'Setting up standalone Sim Studio...'
|
spinner.text = 'Setting up standalone Sim Studio...'
|
||||||
@@ -176,7 +207,9 @@ function checkIfInProjectDirectory(): boolean {
|
|||||||
packageJson.name === 'sim' ||
|
packageJson.name === 'sim' ||
|
||||||
packageJson.name === 'sim-studio' ||
|
packageJson.name === 'sim-studio' ||
|
||||||
(packageJson.dependencies &&
|
(packageJson.dependencies &&
|
||||||
(packageJson.dependencies['next'] || packageJson.dependencies['@sim/cli']))
|
(packageJson.dependencies['next'] ||
|
||||||
|
packageJson.dependencies['@sim/cli'] ||
|
||||||
|
packageJson.dependencies['sim-studio-cli']))
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sim-studio-standalone",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "Standalone server for Sim Studio",
|
|
||||||
"main": "server.js",
|
|
||||||
"dependencies": {
|
|
||||||
"express": "^4.18.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "node server.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sim Studio Standalone Server
|
|
||||||
*
|
|
||||||
* This is a simplified server that serves the pre-built Sim Studio app
|
|
||||||
* and enables localStorage mode automatically.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const express = require('express')
|
|
||||||
const path = require('path')
|
|
||||||
const fs = require('fs')
|
|
||||||
const { createServer } = require('http')
|
|
||||||
const { parse } = require('url')
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
const PORT = process.env.SIM_STUDIO_PORT || 3000
|
|
||||||
const PUBLIC_DIR = path.join(__dirname, 'public')
|
|
||||||
const HTML_FILE = path.join(PUBLIC_DIR, 'index.html')
|
|
||||||
|
|
||||||
// Create Express app
|
|
||||||
const app = express()
|
|
||||||
|
|
||||||
// Set localStorage environment variable in HTML
|
|
||||||
const injectLocalStorageScript = (html) => {
|
|
||||||
const script = `
|
|
||||||
<script>
|
|
||||||
// Set localStorage flag for Sim Studio
|
|
||||||
localStorage.setItem('USE_LOCAL_STORAGE', 'true');
|
|
||||||
console.log('Sim Studio running in local storage mode');
|
|
||||||
</script>
|
|
||||||
`
|
|
||||||
|
|
||||||
// Insert script right before the closing </head> tag
|
|
||||||
return html.replace('</head>', `${script}</head>`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Middleware to inject localStorage flag
|
|
||||||
app.use((req, res, next) => {
|
|
||||||
if (req.path === '/' || req.path.endsWith('.html')) {
|
|
||||||
const originalSend = res.send
|
|
||||||
res.send = function (body) {
|
|
||||||
if (typeof body === 'string' && body.includes('</head>')) {
|
|
||||||
body = injectLocalStorageScript(body)
|
|
||||||
}
|
|
||||||
return originalSend.call(this, body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Serve static files
|
|
||||||
app.use(express.static(PUBLIC_DIR))
|
|
||||||
|
|
||||||
// SPA fallback - all routes not matched should serve index.html
|
|
||||||
app.get('*', (req, res) => {
|
|
||||||
res.sendFile(HTML_FILE)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Start the server
|
|
||||||
app.listen(PORT, () => {
|
|
||||||
console.log(`
|
|
||||||
┌────────────────────────────────────────────────────┐
|
|
||||||
│ │
|
|
||||||
│ 🚀 Sim Studio is running in standalone mode! │
|
|
||||||
│ │
|
|
||||||
│ 🌐 Local: http://localhost:${PORT} ${PORT.toString().length < 4 ? ' ' : ''}│
|
|
||||||
│ │
|
|
||||||
│ 💾 Using localStorage for all data │
|
|
||||||
│ 🔄 All changes will be saved in your browser │
|
|
||||||
│ │
|
|
||||||
│ Press Ctrl+C to stop the server │
|
|
||||||
│ │
|
|
||||||
└────────────────────────────────────────────────────┘
|
|
||||||
`)
|
|
||||||
})
|
|
||||||
@@ -1,92 +1,278 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build Standalone Distribution
|
* Build Standalone App Script
|
||||||
*
|
*
|
||||||
* This script builds a standalone distribution of Sim Studio that can be downloaded
|
* This script builds a standalone version of Sim Studio that can be run without a database.
|
||||||
* and run by the CLI with `npx sim`.
|
* It creates a tarball that includes:
|
||||||
*
|
* 1. A pre-built Next.js application
|
||||||
* The standalone package includes:
|
* 2. A simple Express server to serve the application
|
||||||
* - Pre-built Next.js static export
|
* 3. Configuration to use browser localStorage for data persistence
|
||||||
* - Simplified Express server
|
|
||||||
* - Configured to use localStorage instead of a database
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const { execSync } = require('child_process')
|
const { execSync } = require('child_process')
|
||||||
const packageJson = require('../package.json')
|
const tar = require('tar')
|
||||||
|
const crypto = require('crypto')
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
const STANDALONE_DIR = path.join(__dirname, '../standalone-dist')
|
const ROOT_DIR = path.resolve(__dirname, '..')
|
||||||
const SOURCE_DIR = path.join(__dirname, '..')
|
const STANDALONE_DIR = path.join(ROOT_DIR, 'packages/@sim/cli/standalone')
|
||||||
const OUTPUT_TARBALL = path.join(__dirname, '../sim-standalone.tar.gz')
|
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')
|
// Ensure the standalone directory exists
|
||||||
|
if (!fs.existsSync(STANDALONE_DIR)) {
|
||||||
// Clean up if the directory exists
|
fs.mkdirSync(STANDALONE_DIR, { recursive: true })
|
||||||
if (fs.existsSync(STANDALONE_DIR)) {
|
|
||||||
console.log('Cleaning up previous build...')
|
|
||||||
fs.rmSync(STANDALONE_DIR, { recursive: true, force: 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(STANDALONE_DIR, { recursive: true })
|
||||||
fs.mkdirSync(path.join(STANDALONE_DIR, 'public'), { recursive: true })
|
|
||||||
|
|
||||||
// Build Next.js static export
|
// Build the Next.js app with local storage mode
|
||||||
console.log('Building Next.js static export...')
|
console.log('Building Next.js app in standalone mode...')
|
||||||
try {
|
try {
|
||||||
// Set environment variable for static export with localStorage
|
// Instead of using static export, we'll build a regular Next.js app
|
||||||
process.env.USE_LOCAL_STORAGE = 'true'
|
// and then copy the necessary files to run it with a simple Express server
|
||||||
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE = 'true'
|
|
||||||
|
|
||||||
// Build the app
|
|
||||||
execSync('npm run build', {
|
execSync('npm run build', {
|
||||||
cwd: SOURCE_DIR,
|
cwd: ROOT_DIR,
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
USE_LOCAL_STORAGE: 'true',
|
USE_LOCAL_STORAGE: 'true',
|
||||||
NEXT_PUBLIC_USE_LOCAL_STORAGE: 'true',
|
NEXT_PUBLIC_USE_LOCAL_STORAGE: 'true',
|
||||||
|
DISABLE_DB_SYNC: 'true',
|
||||||
NODE_ENV: 'production',
|
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) {
|
} 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)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy standalone server files
|
// Create a package.json for the standalone app
|
||||||
console.log('Copying standalone server files...')
|
console.log('Creating package.json for standalone app...')
|
||||||
fs.copyFileSync(
|
const packageJson = {
|
||||||
path.join(SOURCE_DIR, 'packages/@sim/cli/standalone/server.js'),
|
name: 'sim-studio-standalone',
|
||||||
path.join(STANDALONE_DIR, 'server.js')
|
version: '0.1.0',
|
||||||
)
|
private: true,
|
||||||
fs.copyFileSync(
|
scripts: {
|
||||||
path.join(SOURCE_DIR, 'packages/@sim/cli/standalone/package.json'),
|
start: 'node server.js',
|
||||||
path.join(STANDALONE_DIR, 'package.json')
|
},
|
||||||
)
|
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...')
|
console.log('Creating tarball...')
|
||||||
try {
|
try {
|
||||||
execSync(`tar -czf "${OUTPUT_TARBALL}" -C "${STANDALONE_DIR}" .`, {
|
// Remove any .env files before creating the tarball
|
||||||
stdio: 'inherit',
|
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}`)
|
// Create the tarball
|
||||||
console.log(`📦 Size: ${(fs.statSync(OUTPUT_TARBALL).size / (1024 * 1024)).toFixed(2)} MB`)
|
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) {
|
} catch (error) {
|
||||||
console.error('Error creating tarball:', error)
|
console.error('Failed to create tarball:', error)
|
||||||
process.exit(1)
|
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)
|
||||||
|
})
|
||||||
@@ -17,6 +17,9 @@ export interface SyncConfig {
|
|||||||
syncInterval?: number
|
syncInterval?: number
|
||||||
onSyncSuccess?: (response: any) => void
|
onSyncSuccess?: (response: any) => void
|
||||||
onSyncError?: (error: any) => void
|
onSyncError?: (error: any) => void
|
||||||
|
|
||||||
|
// Local storage key for standalone mode
|
||||||
|
localStorageKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SYNC_CONFIG: Partial<SyncConfig> = {
|
export const DEFAULT_SYNC_CONFIG: Partial<SyncConfig> = {
|
||||||
@@ -42,6 +45,38 @@ export async function performSync(config: SyncConfig): Promise<boolean> {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if we're in local storage mode
|
||||||
|
const useLocalStorage =
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(window.localStorage.getItem('USE_LOCAL_STORAGE') === 'true' ||
|
||||||
|
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE === 'true' ||
|
||||||
|
process.env.DISABLE_DB_SYNC === 'true')
|
||||||
|
|
||||||
|
if (useLocalStorage && config.localStorageKey) {
|
||||||
|
// In local storage mode, save directly to localStorage
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
config.localStorageKey,
|
||||||
|
JSON.stringify({
|
||||||
|
data: payload,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
if (config.onSyncSuccess) {
|
||||||
|
config.onSyncSuccess({ success: true, message: 'Saved to local storage' })
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
if (config.onSyncError) {
|
||||||
|
config.onSyncError(error)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not in local storage mode or no localStorageKey provided, use API
|
||||||
return await sendWithRetry(config.endpoint, payload, config)
|
return await sendWithRetry(config.endpoint, payload, config)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (config.onSyncError) {
|
if (config.onSyncError) {
|
||||||
|
|||||||
@@ -40,6 +40,21 @@ export async function initializeSyncManagers(): Promise<boolean> {
|
|||||||
managers = [workflowSync, environmentSync]
|
managers = [workflowSync, environmentSync]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Check if we're in local storage mode
|
||||||
|
const useLocalStorage =
|
||||||
|
typeof window !== 'undefined' &&
|
||||||
|
(window.localStorage.getItem('USE_LOCAL_STORAGE') === 'true' ||
|
||||||
|
process.env.NEXT_PUBLIC_USE_LOCAL_STORAGE === 'true' ||
|
||||||
|
process.env.DISABLE_DB_SYNC === 'true')
|
||||||
|
|
||||||
|
if (useLocalStorage) {
|
||||||
|
console.log('Running in local storage mode - skipping DB sync')
|
||||||
|
// In local storage mode, we don't need to fetch from DB
|
||||||
|
// Just load from localStorage directly
|
||||||
|
initialized = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch data from DB on initialization to replace local storage
|
// Fetch data from DB on initialization to replace local storage
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
fetchEnvironmentVariables(),
|
fetchEnvironmentVariables(),
|
||||||
@@ -50,6 +65,9 @@ export async function initializeSyncManagers(): Promise<boolean> {
|
|||||||
initialized = true
|
initialized = true
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Error initializing sync managers:', error)
|
||||||
|
// Even if there's an error, mark as initialized so the app can continue
|
||||||
|
initialized = true
|
||||||
return false
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
initializing = false
|
initializing = false
|
||||||
|
|||||||
Reference in New Issue
Block a user