feat(npx): added scaffolding for 'npx sim' command that relies on localStorage only, can be ran from anywhere with Nodejs

This commit is contained in:
Waleed Latif
2025-03-04 11:45:55 -08:00
parent d309176c5e
commit b3347afa4e
34 changed files with 3835 additions and 90 deletions

View File

@@ -16,6 +16,8 @@ Thank you for your interest in contributing to Sim Studio! Our goal is to provid
- [Local Development Setup](#local-development-setup)
- [License](#license)
- [Adding New Blocks and Tools](#adding-new-blocks-and-tools)
- [Local Storage Mode](#local-storage-mode)
- [CLI](#cli)
---
@@ -391,4 +393,54 @@ Happy coding!
---
## Local Storage Mode
Sim Studio supports a local storage mode that uses the browser's localStorage instead of a database. This is particularly useful for:
- Quick demos and testing
- Using the `npx sim` CLI
- Development without setting up a database
- Creating shareable examples
To enable local storage mode:
1. Set the environment variable: `USE_LOCAL_STORAGE=true`
2. Start the application: `npm run dev`
All data will be stored in the browser's localStorage. This means:
- Data persists between browser sessions
- Different browsers (Chrome vs. Firefox) will have separate data stores
- Database migrations and schema changes won't affect local storage
### Developing the CLI
Sim Studio includes a CLI package that allows users to quickly start the application with `npx sim`. To develop the CLI:
1. Build the CLI: `npm run cli:build`
2. Test the CLI: `npm run cli:start`
3. Make changes in the `packages/@sim/cli` directory
4. Publish new versions: `npm run cli:publish` (requires npm permissions)
The CLI automatically enables local storage mode when running.
### Building the Standalone Version
The `npx sim` command downloads and runs a pre-built standalone version of Sim Studio. To build this standalone version:
1. Run `npm run build:standalone` from the project root
2. This creates a tarball (`sim-standalone.tar.gz`) containing:
- A pre-built static export of the Next.js application
- A simple Express server to serve the static files
- Configuration for localStorage mode
3. To release a new version:
- Upload the tarball to a GitHub release
- Update the `DOWNLOAD_URL` in `packages/@sim/cli/src/commands/start.ts`
- Update the `STANDALONE_VERSION` constant if needed
- Publish the CLI package: `npm run cli:publish`
---
Thank you for taking the time to contribute to Sim Studio. We truly appreciate your efforts and look forward to collaborating with you!

View File

@@ -1,43 +1,65 @@
# Sim Studio
[![Twitter](https://img.shields.io/twitter/follow/simstudio?style=social)](https://x.com/simstudioai) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
**Sim Studio** is a powerful, user-friendly platform that allows developers and agents to build, test, and optimize agentic workflows.
<div align="center">
<img src="https://imgur.com/a/QnSEYrN" alt="Sim Studio Banner" width="600px" />
<p><b>Build, optimize, and test agent workflows with a powerful visual interface.</b></p>
<p>
<a href="https://github.com/simstudioai/sim/stargazers">
<img src="https://img.shields.io/github/stars/simstudioai/sim?style=flat-square" alt="Stars" />
</a>
<a href="https://github.com/simstudioai/sim/network/members">
<img src="https://img.shields.io/github/forks/simstudioai/sim?style=flat-square" alt="Forks" />
</a>
<a href="https://github.com/simstudioai/sim/issues">
<img src="https://img.shields.io/github/issues/simstudioai/sim?style=flat-square" alt="Issues" />
</a>
<a href="https://github.com/simstudioai/sim/blob/main/LICENSE">
<img src="https://img.shields.io/github/license/simstudioai/sim?style=flat-square" alt="License" />
</a>
<a href="https://discord.gg/rTHJynCD">
<img src="https://img.shields.io/discord/1234567890?style=flat-square&label=Discord" alt="Discord" />
</a>
</p>
</div>
## Run
1. Self-host
2. [Join the Waitlist](https://simstudio.ai) for the cloud-hosted beta
## Quick Start
## 🚀 Quick Start
The quickest way to get Sim Studio running locally:
### Try Instantly with npx
1. **Clone the repository**
Sim Studio now supports a quick start option with zero installation required:
```bash
git clone https://github.com/simstudioai/sim.git
npx sim
```
This downloads and runs Sim Studio with browser localStorage for data persistence. Visit http://localhost:3000 to start building workflows immediately!
### Docker Setup (For Development)
For a full development environment with database support:
```bash
# Clone the repository
git clone https://github.com/YOUR_USERNAME/sim.git
cd sim
```
2. **Start with Docker**
```bash
docker compose up -d
```
Or use the convenience script for automatic setup:
```bash
chmod +x start_simstudio_docker.sh
# Start the Docker environment
./start_simstudio_docker.sh
```
This will start Sim Studio at http://localhost:3000 with a local PostgreSQL database.
### VS Code Dev Container
3. **[Optional] Configure Your Environment**
For the best development experience:
In `.env`, configure Authentication secrets (required for login functionality) and optionally a Resend API key (for authentication emails).
1. Install the VS Code Remote - Containers extension
2. Open the project in VS Code
3. Click "Reopen in Container" when prompted
4. Run `sim-start` in the terminal
## Development Options

View File

@@ -1,12 +1,23 @@
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
// In production, use the Vercel-generated POSTGRES_URL
// In development, use the direct DATABASE_URL
const connectionString = process.env.POSTGRES_URL || process.env.DATABASE_URL!
// Check if we're in local storage mode (CLI usage with npx sim)
const isLocalStorage = process.env.USE_LOCAL_STORAGE === 'true'
// Disable prefetch as it is not supported for "Transaction" pool mode
const client = postgres(connectionString, {
// Only connect to the database if we're not in local storage mode
let db: ReturnType<typeof drizzle> | null = null
if (!isLocalStorage) {
// In production, use the Vercel-generated POSTGRES_URL
// In development, use the direct DATABASE_URL
const connectionString = process.env.POSTGRES_URL || process.env.DATABASE_URL!
// Disable prefetch as it is not supported for "Transaction" pool mode
const client = postgres(connectionString, {
prepare: false,
})
export const db = drizzle(client)
})
db = drizzle(client)
}
// Export the database client or a null value if in local storage mode
export { db }

28
lib/storage.ts Normal file
View File

@@ -0,0 +1,28 @@
'use client'
/**
* Storage detection helper that determines if we should use local storage
* This is used when running via the CLI with `npx sim`
*/
// Check if we should use local storage based on environment variable
export const useLocalStorage = () => {
// In client components, check for the environment variable in localStorage
// This is set by the CLI when running with `npx sim`
if (typeof window !== 'undefined') {
return localStorage.getItem('USE_LOCAL_STORAGE') === 'true'
}
// In server components/API routes, check process.env
return process.env.USE_LOCAL_STORAGE === 'true'
}
// Export helpers for components to use
export const storageMode = {
get isLocal() {
return useLocalStorage()
},
get isDatabase() {
return !useLocalStorage()
},
}

View File

@@ -1,10 +1,21 @@
import type { NextConfig } from 'next'
// Check if we're building for standalone distribution
const isStandaloneBuild = process.env.USE_LOCAL_STORAGE === 'true'
const nextConfig: NextConfig = {
devIndicators: false,
images: {
domains: ['avatars.githubusercontent.com'],
// Enable static image optimization for standalone export
unoptimized: isStandaloneBuild,
},
// Use 'export' for standalone builds, 'standalone' for regular builds
output: isStandaloneBuild ? 'export' : 'standalone',
// Only include headers when not building for standalone export
...(isStandaloneBuild
? {}
: {
async headers() {
return [
{
@@ -37,7 +48,7 @@ const nextConfig: NextConfig = {
},
]
},
output: 'standalone',
}),
}
export default nextConfig

2345
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,13 @@
"db:studio": "drizzle-kit studio",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
"test:coverage": "jest --coverage",
"cli:build": "npm run build -w @sim/cli",
"cli:dev": "npm run build -w @sim/cli && cd packages/@sim/cli && node ./dist/index.js",
"cli:publish": "cd packages/@sim/cli && npm publish --access public",
"cli:start": "cd packages/@sim/cli && node ./dist/index.js start",
"build:standalone": "node scripts/build-standalone.js",
"build:cli": "npm run cli:build && npm run build:standalone"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.38.0",
@@ -47,6 +53,7 @@
"lucide-react": "^0.469.0",
"next": "^15.2.0",
"openai": "^4.85.4",
"ora": "^8.2.0",
"postgres": "^3.4.5",
"prismjs": "^1.29.0",
"react": "^18.2.0",
@@ -87,5 +94,8 @@
"*.{js,jsx,ts,tsx,json,css,scss,md}": [
"prettier --write"
]
}
},
"workspaces": [
"packages/@sim/*"
]
}

101
packages/@sim/cli/README.md Normal file
View File

@@ -0,0 +1,101 @@
# 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.
## Quick Start
```bash
# Run Sim Studio with default settings
npx sim
# Start with custom port
npx sim start -p 8080
# Get help
npx sim help
```
## Features
- **Zero Configuration**: Get started immediately with `npx sim`
- **Local Storage**: Works entirely in the browser, no database required
- **Persistence**: Your workflows and data persist between sessions
- **Familiar Experience**: All the power of Sim Studio in a simplified package
## Commands
- `sim` - Start Sim Studio with default settings
- `sim start` - Start Sim Studio with options
- `sim version` - Display version information
- `sim help` - Show help and usage information
## Options
- `-p, --port <port>` - Specify port (default: 3000)
- `-d, --debug` - Enable debug mode
- `-v, --version` - Show version information
- `-h, --help` - Show help information
## Local Storage Mode
When running Sim Studio via the CLI, all data is stored using the browser's localStorage. This means:
- Your workflows persist between browser sessions
- No database configuration is required
- Data is stored locally on your device
- Multiple users can't share the same workflows (single-user mode)
## Advanced Usage
If you need multi-user capabilities or want to store data in a database, consider:
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.

14
packages/@sim/cli/bin/sim.js Executable file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env node
// This file is the entry point for the 'sim' command
try {
require('../dist/index.js')
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
console.error('Sim CLI has not been built. Please run npm run build first.')
process.exit(1)
} else {
console.error('An error occurred while starting Sim CLI:', error)
process.exit(1)
}
}

View File

@@ -0,0 +1,4 @@
/**
* Help command displays the logo and usage information
*/
export declare function help(): void;

43
packages/@sim/cli/dist/commands/help.js vendored Normal file
View File

@@ -0,0 +1,43 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.help = help;
const chalk_1 = __importDefault(require("chalk"));
const logo_1 = require("../utils/logo");
/**
* Help command displays the logo and usage information
*/
function help() {
// Display logo
console.log(logo_1.logo);
// Display help text
console.log(`
${chalk_1.default.bold('USAGE')}
${chalk_1.default.cyan('sim')} Start Sim Studio with default settings
${chalk_1.default.cyan('sim start')} Start Sim Studio with options
${chalk_1.default.cyan('sim version')} Display version information
${chalk_1.default.cyan('sim help')} Show this help information
${chalk_1.default.bold('OPTIONS')}
${chalk_1.default.cyan('-p, --port <port>')} Specify port (default: 3000)
${chalk_1.default.cyan('-d, --debug')} Enable debug mode
${chalk_1.default.cyan('-v, --version')} Show version information
${chalk_1.default.cyan('-h, --help')} Show help information
${chalk_1.default.bold('EXAMPLES')}
${chalk_1.default.gray('# Start with default settings')}
${chalk_1.default.cyan('$ sim')}
${chalk_1.default.gray('# Start on a specific port')}
${chalk_1.default.cyan('$ sim start --port 8080')}
${chalk_1.default.gray('# Start with debug logging')}
${chalk_1.default.cyan('$ sim start --debug')}
${chalk_1.default.bold('DOCUMENTATION')}
${chalk_1.default.gray('For more information:')}
https://github.com/simstudioai/sim
`);
}

View File

@@ -0,0 +1,9 @@
interface StartOptions {
port: string;
debug: boolean;
}
/**
* Start command that launches Sim Studio using local storage
*/
export declare function start(options: StartOptions): Promise<import("child_process").ChildProcess>;
export {};

267
packages/@sim/cli/dist/commands/start.js vendored Normal file
View File

@@ -0,0 +1,267 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.start = start;
const child_process_1 = require("child_process");
const chalk_1 = __importDefault(require("chalk"));
const config_1 = require("../utils/config");
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const tar_1 = require("tar");
const https_1 = __importDefault(require("https"));
const fs_2 = require("fs");
const child_process_2 = require("child_process");
// Constants for standalone app
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_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 STANDALONE_VERSION = '0.1.0';
/**
* Start command that launches Sim Studio using local storage
*/
async function start(options) {
// Update config with provided options
config_1.config.set('port', options.port);
config_1.config.set('debug', options.debug);
config_1.config.set('lastRun', new Date().toISOString());
const port = options.port || '3000';
const debug = options.debug || false;
// Dynamically import ora
const oraModule = await Promise.resolve().then(() => __importStar(require('ora')));
const ora = oraModule.default;
// Show starting message
const spinner = ora(`Starting Sim Studio on port ${port}...`).start();
try {
// Set environment variables for using local storage
const env = {
...process.env,
PORT: port,
USE_LOCAL_STORAGE: 'true', // Key environment variable to switch to local storage
NODE_ENV: debug ? 'development' : 'production',
DEBUG: debug ? '*' : undefined,
};
// Try to find the main package.json to determine if we're running from within the repo
// or as an installed npm package
const isInProjectDirectory = checkIfInProjectDirectory();
let simProcess;
if (isInProjectDirectory) {
// Running from within the project directory - we'll use the existing
// Next.js setup directly
spinner.text = 'Detected Sim Studio project, starting with local configuration...';
simProcess = (0, child_process_1.spawn)('npm', ['run', 'dev'], {
env,
stdio: 'inherit',
shell: true
});
}
else {
// Running from outside the project via npx - we'll download and start a standalone version
spinner.text = 'Setting up standalone Sim Studio...';
// Create the .sim-studio directory if it doesn't exist
if (!fs_1.default.existsSync(SIM_HOME_DIR)) {
fs_1.default.mkdirSync(SIM_HOME_DIR, { recursive: true });
}
// Check if we already have the standalone version
let needsDownload = true;
if (fs_1.default.existsSync(SIM_VERSION_FILE)) {
try {
const versionInfo = JSON.parse(fs_1.default.readFileSync(SIM_VERSION_FILE, 'utf8'));
if (versionInfo.version === STANDALONE_VERSION) {
needsDownload = false;
}
}
catch (error) {
// If there's an error reading the version file, download again
needsDownload = true;
}
}
// Download and extract if needed
if (needsDownload) {
try {
await downloadStandaloneApp(spinner);
}
catch (error) {
spinner.fail(`Failed to download Sim Studio: ${error instanceof Error ? error.message : String(error)}`);
console.log(`\n${chalk_1.default.yellow('⚠️')} If you're having network issues, you can try:
1. Check your internet connection
2. Try again later
3. Run Sim Studio directly from a cloned repository`);
process.exit(1);
}
}
else {
spinner.text = 'Using cached Sim Studio standalone version...';
}
// Start the standalone app
spinner.text = 'Starting Sim Studio standalone...';
// Make sure the standalone directory exists
if (!fs_1.default.existsSync(SIM_STANDALONE_DIR) || !fs_1.default.existsSync(path_1.default.join(SIM_STANDALONE_DIR, 'server.js'))) {
spinner.fail('Standalone app files are missing. Re-run to download again.');
// Force a fresh download next time
if (fs_1.default.existsSync(SIM_VERSION_FILE)) {
fs_1.default.unlinkSync(SIM_VERSION_FILE);
}
process.exit(1);
}
// Start the standalone Node.js server
const standaloneEnv = {
...env,
SIM_STUDIO_PORT: port,
};
simProcess = (0, child_process_1.spawn)('node', ['server.js'], {
cwd: SIM_STANDALONE_DIR,
env: standaloneEnv,
stdio: 'inherit',
shell: true
});
}
// Successful start
spinner.succeed(`Sim Studio is running on ${chalk_1.default.cyan(`http://localhost:${port}`)}`);
console.log(`
${chalk_1.default.green('✓')} Using local storage mode - your data will be stored in the browser
${chalk_1.default.green('✓')} Any changes will be persisted between sessions through localStorage
${chalk_1.default.yellow('i')} Press ${chalk_1.default.bold('Ctrl+C')} to stop the server
`);
// Handle process termination
process.on('SIGINT', () => {
console.log(`\n${chalk_1.default.yellow('⚠️')} Shutting down Sim Studio...`);
simProcess.kill('SIGINT');
process.exit(0);
});
// Return the process for testing purposes
return simProcess;
}
catch (error) {
spinner.fail('Failed to start Sim Studio');
console.error(chalk_1.default.red('Error:'), error instanceof Error ? error.message : error);
process.exit(1);
}
}
/**
* Checks if we're running in a Sim Studio project directory
*/
function checkIfInProjectDirectory() {
// Check if we have package.json that looks like a Sim Studio project
try {
const packageJsonPath = path_1.default.join(process.cwd(), 'package.json');
if (fs_1.default.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, 'utf8'));
// Check if it looks like our project
if (packageJson.name === 'sim' ||
packageJson.name === 'sim-studio' ||
(packageJson.dependencies &&
(packageJson.dependencies['next'] || packageJson.dependencies['@sim/cli']))) {
return true;
}
}
// Also check for Next.js app files
const nextConfigPath = path_1.default.join(process.cwd(), 'next.config.js');
const nextTsConfigPath = path_1.default.join(process.cwd(), 'next.config.ts');
if (fs_1.default.existsSync(nextConfigPath) || fs_1.default.existsSync(nextTsConfigPath)) {
return true;
}
}
catch (error) {
// If we can't read/parse package.json, assume we're not in a project directory
}
return false;
}
/**
* Downloads and extracts the standalone app
*/
async function downloadStandaloneApp(spinner) {
return new Promise((resolve, reject) => {
// Create temp directory
const tmpDir = path_1.default.join(os_1.default.tmpdir(), `sim-download-${Date.now()}`);
fs_1.default.mkdirSync(tmpDir, { recursive: true });
const tarballPath = path_1.default.join(tmpDir, 'sim-standalone.tar.gz');
const file = (0, fs_2.createWriteStream)(tarballPath);
spinner.text = 'Downloading Sim Studio...';
// Download the tarball
https_1.default.get(DOWNLOAD_URL, (response) => {
if (response.statusCode !== 200) {
spinner.fail(`Failed to download: ${response.statusCode}`);
return reject(new Error(`Download failed with status code: ${response.statusCode}`));
}
response.pipe(file);
file.on('finish', () => {
file.close();
// Clear the standalone directory if it exists
if (fs_1.default.existsSync(SIM_STANDALONE_DIR)) {
fs_1.default.rmSync(SIM_STANDALONE_DIR, { recursive: true, force: true });
}
// Create the directory
fs_1.default.mkdirSync(SIM_STANDALONE_DIR, { recursive: true });
spinner.text = 'Extracting Sim Studio...';
// Extract the tarball
(0, tar_1.extract)({
file: tarballPath,
cwd: SIM_STANDALONE_DIR
}).then(() => {
// Clean up
fs_1.default.rmSync(tmpDir, { recursive: true, force: true });
// Install dependencies if needed
if (fs_1.default.existsSync(path_1.default.join(SIM_STANDALONE_DIR, 'package.json'))) {
spinner.text = 'Installing dependencies...';
try {
(0, child_process_2.execSync)('npm install --production', {
cwd: SIM_STANDALONE_DIR,
stdio: 'ignore'
});
}
catch (error) {
spinner.warn('Error installing dependencies, but trying to continue...');
}
}
// Save version info
fs_1.default.writeFileSync(SIM_VERSION_FILE, JSON.stringify({ version: STANDALONE_VERSION, installedAt: new Date().toISOString() }));
resolve();
}).catch((err) => {
spinner.fail('Extraction failed');
reject(err);
});
});
}).on('error', (err) => {
fs_1.default.unlink(tarballPath, () => { });
spinner.fail('Download failed');
reject(err);
});
});
}

View File

@@ -0,0 +1,4 @@
/**
* Version command displays the current version of the CLI
*/
export declare function version(): void;

View File

@@ -0,0 +1,19 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = version;
const chalk_1 = __importDefault(require("chalk"));
/**
* Version command displays the current version of the CLI
*/
function version() {
const pkg = require('../../package.json');
console.log(`
${chalk_1.default.bold('Sim Studio CLI')} ${chalk_1.default.green(`v${pkg.version}`)}
${chalk_1.default.gray('Platform:')} ${process.platform}
${chalk_1.default.gray('Node Version:')} ${process.version}
${chalk_1.default.gray('CLI Path:')} ${__dirname}
`);
}

2
packages/@sim/cli/dist/index.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env node
export {};

68
packages/@sim/cli/dist/index.js vendored Normal file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const chalk_1 = __importDefault(require("chalk"));
const commander_1 = require("commander");
const update_notifier_1 = __importDefault(require("update-notifier"));
const start_1 = require("./commands/start");
const help_1 = require("./commands/help");
const version_1 = require("./commands/version");
const logo_1 = require("./utils/logo");
const config_1 = require("./utils/config");
// Package info for version checking
const pkg = require('../package.json');
// Check for updates
(0, update_notifier_1.default)({ pkg }).notify();
// Create program
const program = new commander_1.Command();
// Initialize CLI
async function main() {
// Configure the CLI
program
.name('sim')
.description('Sim Studio CLI')
.version(pkg.version, '-v, --version', 'Output the current version')
.helpOption('-h, --help', 'Display help for command')
.on('--help', () => (0, help_1.help)())
.action(() => {
// Default command (no args) runs start with default options
(0, start_1.start)({ port: config_1.config.get('port'), debug: config_1.config.get('debug') });
});
// Start command
program
.command('start')
.description('Start Sim Studio with local storage')
.option('-p, --port <port>', 'Port to run on', config_1.config.get('port'))
.option('-d, --debug', 'Enable debug mode', config_1.config.get('debug'))
.action((options) => {
(0, start_1.start)(options);
});
// Version command
program
.command('version')
.description('Show detailed version information')
.action(() => {
(0, version_1.version)();
});
// Help command
program
.command('help')
.description('Display help information')
.action(() => {
(0, help_1.help)();
});
// Display logo if not in help mode
if (!process.argv.includes('--help') && !process.argv.includes('-h')) {
console.log(logo_1.logo);
}
// Parse arguments
program.parse(process.argv);
}
// Run the CLI
main().catch((error) => {
console.error(chalk_1.default.red('Error:'), error);
process.exit(1);
});

View File

@@ -0,0 +1,8 @@
import Conf from 'conf';
interface ConfigSchema {
port: string;
debug: boolean;
lastRun: string;
}
export declare const config: Conf<ConfigSchema>;
export {};

16
packages/@sim/cli/dist/utils/config.js vendored Normal file
View File

@@ -0,0 +1,16 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = void 0;
const conf_1 = __importDefault(require("conf"));
// Create a config instance with default values
exports.config = new conf_1.default({
projectName: 'sim-studio',
defaults: {
port: '3000',
debug: false,
lastRun: new Date().toISOString(),
},
});

View File

@@ -0,0 +1,4 @@
/**
* ASCII art logo for Sim Studio
*/
export declare const logo: string;

22
packages/@sim/cli/dist/utils/logo.js vendored Normal file
View File

@@ -0,0 +1,22 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.logo = void 0;
const chalk_1 = __importDefault(require("chalk"));
/**
* ASCII art logo for Sim Studio
*/
exports.logo = `
${chalk_1.default.bold(chalk_1.default.magenta(`
███████╗██╗███╗ ███╗ ███████╗████████╗██╗ ██╗██████╗ ██╗ ██████╗
██╔════╝██║████╗ ████║ ██╔════╝╚══██╔══╝██║ ██║██╔══██╗██║██╔═══██╗
███████╗██║██╔████╔██║ ███████╗ ██║ ██║ ██║██║ ██║██║██║ ██║
╚════██║██║██║╚██╔╝██║ ╚════██║ ██║ ██║ ██║██║ ██║██║██║ ██║
███████║██║██║ ╚═╝ ██║ ███████║ ██║ ╚██████╔╝██████╔╝██║╚██████╔╝
╚══════╝╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝
`))}
${chalk_1.default.cyan('Build, optimize, and test agent workflows with a powerful visual interface')}
`;

View File

@@ -0,0 +1,54 @@
{
"name": "@sim/cli",
"version": "0.1.0",
"description": "CLI tool for Sim Studio - easily start, build and test agent workflows",
"license": "MIT",
"author": "Sim Studio Team",
"main": "dist/index.js",
"type": "commonjs",
"bin": {
"sim": "./bin/sim.js"
},
"files": [
"bin",
"dist",
"README.md"
],
"scripts": {
"build": "tsc",
"start": "node bin/sim.js",
"dev": "ts-node src/index.ts",
"clean": "rimraf dist",
"prepublishOnly": "npm run clean && npm run build"
},
"keywords": [
"sim",
"sim-studio",
"workflow",
"automation",
"cli",
"agent"
],
"dependencies": {
"chalk": "^4.1.2",
"commander": "^11.1.0",
"conf": "^10.2.0",
"dotenv": "^16.4.7",
"inquirer": "^8.2.6",
"ora": "^8.2.0",
"tar": "^6.2.1",
"update-notifier": "^5.1.0"
},
"devDependencies": {
"@types/inquirer": "^8.2.10",
"@types/node": "^20.11.30",
"@types/tar": "^6.1.11",
"@types/update-notifier": "^5.1.0",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"typescript": "^5.7.3"
},
"engines": {
"node": ">=16.0.0"
}
}

View File

@@ -0,0 +1,39 @@
import chalk from 'chalk'
import { logo } from '../utils/logo'
/**
* Help command displays the logo and usage information
*/
export function help() {
// Display logo
console.log(logo)
// Display help text
console.log(`
${chalk.bold('USAGE')}
${chalk.cyan('sim')} Start Sim Studio with default settings
${chalk.cyan('sim start')} Start Sim Studio with options
${chalk.cyan('sim version')} Display version information
${chalk.cyan('sim help')} Show this help information
${chalk.bold('OPTIONS')}
${chalk.cyan('-p, --port <port>')} Specify port (default: 3000)
${chalk.cyan('-d, --debug')} Enable debug mode
${chalk.cyan('-v, --version')} Show version information
${chalk.cyan('-h, --help')} Show help information
${chalk.bold('EXAMPLES')}
${chalk.gray('# Start with default settings')}
${chalk.cyan('$ sim')}
${chalk.gray('# Start on a specific port')}
${chalk.cyan('$ sim start --port 8080')}
${chalk.gray('# Start with debug logging')}
${chalk.cyan('$ sim start --debug')}
${chalk.bold('DOCUMENTATION')}
${chalk.gray('For more information:')}
https://github.com/simstudioai/sim
`)
}

View File

@@ -0,0 +1,286 @@
import chalk from 'chalk'
import { spawn } from 'child_process'
import { execSync } from 'child_process'
import fs from 'fs'
import { createWriteStream } from 'fs'
import https from 'https'
import type { Ora } from 'ora'
import os from 'os'
import path from 'path'
import { extract } from 'tar'
import { config } from '../utils/config'
interface StartOptions {
port: string
debug: boolean
}
// Constants for standalone app
const SIM_HOME_DIR = path.join(os.homedir(), '.sim-studio')
const SIM_STANDALONE_DIR = path.join(SIM_HOME_DIR, 'standalone')
const SIM_VERSION_FILE = path.join(SIM_HOME_DIR, 'version.json')
const DOWNLOAD_URL =
'https://github.com/simstudioai/sim/releases/download/v0.1.0/sim-standalone.tar.gz'
const STANDALONE_VERSION = '0.1.0'
/**
* Start command that launches Sim Studio using local storage
*/
export async function start(options: StartOptions) {
// Update config with provided options
config.set('port', options.port)
config.set('debug', options.debug)
config.set('lastRun', new Date().toISOString())
const port = options.port || '3000'
const debug = options.debug || false
// Dynamically import ora
const oraModule = await import('ora')
const ora = oraModule.default
// Show starting message
const spinner = ora(`Starting Sim Studio on port ${port}...`).start()
try {
// Set environment variables for using local storage
const env = {
...process.env,
PORT: port,
USE_LOCAL_STORAGE: 'true', // Key environment variable to switch to local storage
NODE_ENV: debug ? 'development' : 'production',
DEBUG: debug ? '*' : undefined,
}
// Try to find the main package.json to determine if we're running from within the repo
// or as an installed npm package
const isInProjectDirectory = checkIfInProjectDirectory()
let simProcess
if (isInProjectDirectory) {
// Running from within the project directory - we'll use the existing
// Next.js setup directly
spinner.text = 'Detected Sim Studio project, starting with local configuration...'
simProcess = spawn('npm', ['run', 'dev'], {
env,
stdio: 'inherit',
shell: true,
})
} else {
// Running from outside the project via npx - we'll download and start a standalone version
spinner.text = 'Setting up standalone Sim Studio...'
// Create the .sim-studio directory if it doesn't exist
if (!fs.existsSync(SIM_HOME_DIR)) {
fs.mkdirSync(SIM_HOME_DIR, { recursive: true })
}
// Check if we already have the standalone version
let needsDownload = true
if (fs.existsSync(SIM_VERSION_FILE)) {
try {
const versionInfo = JSON.parse(fs.readFileSync(SIM_VERSION_FILE, 'utf8'))
if (versionInfo.version === STANDALONE_VERSION) {
needsDownload = false
}
} catch (error) {
// If there's an error reading the version file, download again
needsDownload = true
}
}
// Download and extract if needed
if (needsDownload) {
try {
await downloadStandaloneApp(spinner)
} catch (error) {
spinner.fail(
`Failed to download Sim Studio: ${error instanceof Error ? error.message : String(error)}`
)
console.log(`\n${chalk.yellow('⚠️')} If you're having network issues, you can try:
1. Check your internet connection
2. Try again later
3. Run Sim Studio directly from a cloned repository`)
process.exit(1)
}
} else {
spinner.text = 'Using cached Sim Studio standalone version...'
}
// Start the standalone app
spinner.text = 'Starting Sim Studio standalone...'
// Make sure the standalone directory exists
if (
!fs.existsSync(SIM_STANDALONE_DIR) ||
!fs.existsSync(path.join(SIM_STANDALONE_DIR, 'server.js'))
) {
spinner.fail('Standalone app files are missing. Re-run to download again.')
// Force a fresh download next time
if (fs.existsSync(SIM_VERSION_FILE)) {
fs.unlinkSync(SIM_VERSION_FILE)
}
process.exit(1)
}
// Start the standalone Node.js server
const standaloneEnv = {
...env,
SIM_STUDIO_PORT: port,
}
simProcess = spawn('node', ['server.js'], {
cwd: SIM_STANDALONE_DIR,
env: standaloneEnv,
stdio: 'inherit',
shell: true,
})
}
// Successful start
spinner.succeed(`Sim Studio is running on ${chalk.cyan(`http://localhost:${port}`)}`)
console.log(`
${chalk.green('✓')} Using local storage mode - your data will be stored in the browser
${chalk.green('✓')} Any changes will be persisted between sessions through localStorage
${chalk.yellow('i')} Press ${chalk.bold('Ctrl+C')} to stop the server
`)
// Handle process termination
process.on('SIGINT', () => {
console.log(`\n${chalk.yellow('⚠️')} Shutting down Sim Studio...`)
simProcess.kill('SIGINT')
process.exit(0)
})
// Return the process for testing purposes
return simProcess
} catch (error) {
spinner.fail('Failed to start Sim Studio')
console.error(chalk.red('Error:'), error instanceof Error ? error.message : error)
process.exit(1)
}
}
/**
* Checks if we're running in a Sim Studio project directory
*/
function checkIfInProjectDirectory(): boolean {
// Check if we have package.json that looks like a Sim Studio project
try {
const packageJsonPath = path.join(process.cwd(), 'package.json')
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
// Check if it looks like our project
if (
packageJson.name === 'sim' ||
packageJson.name === 'sim-studio' ||
(packageJson.dependencies &&
(packageJson.dependencies['next'] || packageJson.dependencies['@sim/cli']))
) {
return true
}
}
// Also check for Next.js app files
const nextConfigPath = path.join(process.cwd(), 'next.config.js')
const nextTsConfigPath = path.join(process.cwd(), 'next.config.ts')
if (fs.existsSync(nextConfigPath) || fs.existsSync(nextTsConfigPath)) {
return true
}
} catch (error) {
// If we can't read/parse package.json, assume we're not in a project directory
}
return false
}
/**
* Downloads and extracts the standalone app
*/
async function downloadStandaloneApp(spinner: Ora): Promise<void> {
return new Promise((resolve, reject) => {
// Create temp directory
const tmpDir = path.join(os.tmpdir(), `sim-download-${Date.now()}`)
fs.mkdirSync(tmpDir, { recursive: true })
const tarballPath = path.join(tmpDir, 'sim-standalone.tar.gz')
const file = createWriteStream(tarballPath)
spinner.text = 'Downloading Sim Studio...'
// Download the tarball
https
.get(DOWNLOAD_URL, (response) => {
if (response.statusCode !== 200) {
spinner.fail(`Failed to download: ${response.statusCode}`)
return reject(new Error(`Download failed with status code: ${response.statusCode}`))
}
response.pipe(file)
file.on('finish', () => {
file.close()
// Clear the standalone directory if it exists
if (fs.existsSync(SIM_STANDALONE_DIR)) {
fs.rmSync(SIM_STANDALONE_DIR, { recursive: true, force: true })
}
// Create the directory
fs.mkdirSync(SIM_STANDALONE_DIR, { recursive: true })
spinner.text = 'Extracting Sim Studio...'
// Extract the tarball
extract({
file: tarballPath,
cwd: SIM_STANDALONE_DIR,
})
.then(() => {
// Clean up
fs.rmSync(tmpDir, { recursive: true, force: true })
// Install dependencies if needed
if (fs.existsSync(path.join(SIM_STANDALONE_DIR, 'package.json'))) {
spinner.text = 'Installing dependencies...'
try {
execSync('npm install --production', {
cwd: SIM_STANDALONE_DIR,
stdio: 'ignore',
})
} catch (error) {
spinner.warn('Error installing dependencies, but trying to continue...')
}
}
// Save version info
fs.writeFileSync(
SIM_VERSION_FILE,
JSON.stringify({
version: STANDALONE_VERSION,
installedAt: new Date().toISOString(),
})
)
resolve()
})
.catch((err: Error) => {
spinner.fail('Extraction failed')
reject(err)
})
})
})
.on('error', (err: Error) => {
fs.unlink(tarballPath, () => {})
spinner.fail('Download failed')
reject(err)
})
})
}

View File

@@ -0,0 +1,15 @@
import chalk from 'chalk'
/**
* Version command displays the current version of the CLI
*/
export function version() {
const pkg = require('../../package.json')
console.log(`
${chalk.bold('Sim Studio CLI')} ${chalk.green(`v${pkg.version}`)}
${chalk.gray('Platform:')} ${process.platform}
${chalk.gray('Node Version:')} ${process.version}
${chalk.gray('CLI Path:')} ${__dirname}
`)
}

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env node
import chalk from 'chalk'
import { Command } from 'commander'
import updateNotifier from 'update-notifier'
import { help } from './commands/help'
import { start } from './commands/start'
import { version } from './commands/version'
import { config } from './utils/config'
import { logo } from './utils/logo'
// Package info for version checking
const pkg = require('../package.json')
// Check for updates
updateNotifier({ pkg }).notify()
// Create program
const program = new Command()
// Initialize CLI
async function main() {
// Configure the CLI
program
.name('sim')
.description('Sim Studio CLI')
.version(pkg.version, '-v, --version', 'Output the current version')
.helpOption('-h, --help', 'Display help for command')
.on('--help', () => help())
.action(() => {
// Default command (no args) runs start with default options
start({ port: config.get('port'), debug: config.get('debug') })
})
// Start command
program
.command('start')
.description('Start Sim Studio with local storage')
.option('-p, --port <port>', 'Port to run on', config.get('port'))
.option('-d, --debug', 'Enable debug mode', config.get('debug'))
.action((options) => {
start(options)
})
// Version command
program
.command('version')
.description('Show detailed version information')
.action(() => {
version()
})
// Help command
program
.command('help')
.description('Display help information')
.action(() => {
help()
})
// Display logo if not in help mode
if (!process.argv.includes('--help') && !process.argv.includes('-h')) {
console.log(logo)
}
// Parse arguments
program.parse(process.argv)
}
// Run the CLI
main().catch((error) => {
console.error(chalk.red('Error:'), error)
process.exit(1)
})

View File

@@ -0,0 +1,18 @@
import Conf from 'conf'
// Config schema definition
interface ConfigSchema {
port: string
debug: boolean
lastRun: string
}
// Create a config instance with default values
export const config = new Conf<ConfigSchema>({
projectName: 'sim-studio',
defaults: {
port: '3000',
debug: false,
lastRun: new Date().toISOString(),
},
})

View File

@@ -0,0 +1,19 @@
import chalk from 'chalk'
/**
* ASCII art logo for Sim Studio
*/
export const logo = `
${chalk.bold(
chalk.magenta(`
███████╗██╗███╗ ███╗ ███████╗████████╗██╗ ██╗██████╗ ██╗ ██████╗
██╔════╝██║████╗ ████║ ██╔════╝╚══██╔══╝██║ ██║██╔══██╗██║██╔═══██╗
███████╗██║██╔████╔██║ ███████╗ ██║ ██║ ██║██║ ██║██║██║ ██║
╚════██║██║██║╚██╔╝██║ ╚════██║ ██║ ██║ ██║██║ ██║██║██║ ██║
███████║██║██║ ╚═╝ ██║ ███████║ ██║ ╚██████╔╝██████╔╝██║╚██████╔╝
╚══════╝╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═════╝
`)
)}
${chalk.cyan('Build, optimize, and test agent workflows with a powerful visual interface')}
`

View File

@@ -0,0 +1,16 @@
{
"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"
}
}

View File

@@ -0,0 +1,76 @@
#!/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 │
│ │
└────────────────────────────────────────────────────┘
`)
})

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "Node",
"esModuleInterop": true,
"declaration": true,
"outDir": "./dist",
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

92
scripts/build-standalone.js Executable file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env node
/**
* Build Standalone Distribution
*
* 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
*/
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const packageJson = require('../package.json')
// 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')
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 })
}
// Create standalone directory
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...')
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
execSync('npm run build', {
cwd: SOURCE_DIR,
stdio: 'inherit',
env: {
...process.env,
USE_LOCAL_STORAGE: 'true',
NEXT_PUBLIC_USE_LOCAL_STORAGE: '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)
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 tarball
console.log('Creating tarball...')
try {
execSync(`tar -czf "${OUTPUT_TARBALL}" -C "${STANDALONE_DIR}" .`, {
stdio: 'inherit',
})
console.log(`✅ Standalone distribution created: ${OUTPUT_TARBALL}`)
console.log(`📦 Size: ${(fs.statSync(OUTPUT_TARBALL).size / (1024 * 1024)).toFixed(2)} MB`)
} catch (error) {
console.error('Error creating 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')

33
scripts/setup_cli.sh Normal file
View File

@@ -0,0 +1,33 @@
#!/bin/bash
set -e
echo "Setting up Sim Studio CLI Package..."
# Create directory structure if it doesn't exist
mkdir -p packages/@sim/cli/bin
mkdir -p packages/@sim/cli/src/commands
mkdir -p packages/@sim/cli/src/utils
# Navigate to CLI directory
cd packages/@sim/cli
# Install dependencies
echo "Installing CLI dependencies..."
npm install
# Build the CLI package
echo "Building CLI package..."
npm run build
# Make the CLI executable
chmod +x bin/sim.js
echo "✅ CLI setup complete!"
echo ""
echo "You can now run:"
echo " npm run cli:start - to test the CLI"
echo " npm run cli:dev - to develop the CLI with live reload"
echo " npm run cli:publish - to publish to npm"
echo ""
echo "Try it out with: ./packages/@sim/cli/bin/sim.js"