From 145425a0b11e1f9abe44bfee432929d66cc6c15f Mon Sep 17 00:00:00 2001 From: "Alaa-eddine K." Date: Mon, 30 Jun 2025 21:14:26 +0200 Subject: [PATCH] CLI update + fix for google AI api keys SRE Configuration documentation --- package.json | 2 +- packages/cli/package.json | 2 +- .../cli/src/commands/create/create.index.ts | 142 ++++++++++++------ packages/core/docs/configuration.md | 86 +++++++++++ packages/core/package.json | 2 +- packages/sdk/docs/01-getting-started.md | 48 +++++- packages/sdk/package.json | 2 +- 7 files changed, 227 insertions(+), 57 deletions(-) create mode 100644 packages/core/docs/configuration.md diff --git a/package.json b/package.json index a995d6bc..64ad2ce2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sre", - "version": "0.0.15", + "version": "0.1.0", "description": "", "author": "Alaa-eddine KADDOURI", "license": "MIT", diff --git a/packages/cli/package.json b/packages/cli/package.json index 91a44a7b..28bfa21d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@smythos/cli", - "version": "0.2.16", + "version": "0.2.20", "description": "SmythOS SRE Command Line Interface", "keywords": [ "smythos", diff --git a/packages/cli/src/commands/create/create.index.ts b/packages/cli/src/commands/create/create.index.ts index db3be9e7..89dd6815 100644 --- a/packages/cli/src/commands/create/create.index.ts +++ b/packages/cli/src/commands/create/create.index.ts @@ -37,7 +37,7 @@ const detectApiKeys = () => { const keys: { [key: string]: string | undefined } = { openai: process.env.OPENAI_API_KEY ? '$env(OPENAI_API_KEY)' : undefined, anthropic: process.env.ANTHROPIC_API_KEY ? '$env(ANTHROPIC_API_KEY)' : undefined, - google: process.env.GOOGLE_API_KEY ? '$env(GOOGLE_API_KEY)' : undefined, + googleai: process.env.GOOGLE_API_KEY ? '$env(GOOGLE_API_KEY)' : process.env.GEMINI_API_KEY ? '$env(GEMINI_API_KEY)' : undefined, }; return Object.entries(keys).reduce((acc, [key, value]) => { @@ -153,6 +153,8 @@ async function RunProject(projectNameArg?: string) { .map((k) => chalk.cyan(k)) .join(', '); + const existingVault = fs.existsSync(vaultFile) ? JSON.parse(fs.readFileSync(vaultFile, 'utf8')) : null; + const initialAnswers = await inquirer.prompt([ { type: 'input', @@ -204,9 +206,36 @@ async function RunProject(projectNameArg?: string) { answers.projectName = projectName; + console.log(chalk.yellowBright(`\n===[ Now let's configure Smyth Resources folder ]===`)); + console.log( + `${chalk.gray( + 'Some connectors in SmythOS might need to store data locally, in order to keep things clean, we store all SmythOS related data in a single place.\n' + )}` + ); + + const resourcesAnswers = await inquirer.prompt([ + { + type: 'list', + name: 'smythResources', + message: 'Smyth Resources Folder', + suffix: `\n${chalk.magenta('Location where we can store data like logs, cache, etc. (Use arrow keys ↑↓)')}\n`, + choices: [ + { name: `Shared folder in the ${chalk.underline('user home directory')} (~/.smyth)`, value: path.join(os.homedir(), '.smyth') }, + { + name: `Local folder under ${chalk.underline('project root')} (./${path.basename(answers.targetFolder)}/.smyth)`, + value: path.join(answers.targetFolder, '.smyth'), + }, + ], + default: 0, + }, + ]); + + const isSharedResources = resourcesAnswers.smythResources === path.join(os.homedir(), '.smyth'); + let _useSharedVault = isSharedResources; + let vault: { [key: string]: string } = {}; - let _useSharedVault = false; + //let _useSharedVault = false; console.log(chalk.yellowBright(`\n===[ Now let's set your secrets ]===`)); console.log( @@ -214,7 +243,26 @@ async function RunProject(projectNameArg?: string) { 'SmythOS uses a vault to store your secrets. Set your secrets once, they’ll be securely stored and loaded by the SDK only when needed.This keeps LLM API keys out of your code.\n' )}` ); - + if (_useSharedVault && vaultFile) { + console.log(chalk.magenta(` ℹ We will use Vault file found in your shared folder ${vaultFile}`)); + const existingProviders = Object.keys(existingVault?.default).filter((p) => existingVault?.default[p]); + if (existingProviders.length > 0) { + console.log(chalk.magenta(` ℹ API keys already present in your shared vault: ${existingProviders.join(', ')}`)); + } + vault = { + openai: '#', + anthropic: '#', + googleai: '#', + groq: '#', + togetherai: '#', + xai: '#', + deepseek: '#', + tavily: '#', + scrapfly: '#', + ...(existingVault?.default || {}), + }; + } + /* if (vaultFile) { console.log(chalk.magenta(` ℹ I found an existing shared vault file ${vaultFile}`)); const { useSharedVault } = await inquirer.prompt([ @@ -238,7 +286,9 @@ async function RunProject(projectNameArg?: string) { scrapfly: '#', }; } + */ + /* if (hasDetectedKeys && !_useSharedVault) { const { useDetectedKeys } = await inquirer.prompt([ { @@ -252,50 +302,37 @@ async function RunProject(projectNameArg?: string) { vault = { ...detectedKeys }; } } + */ - const allProviders = ['openai', 'anthropic', 'google']; + const allProviders = ['openai', 'anthropic', 'googleai']; const missingKeyQuestions = allProviders .filter((provider) => !vault[provider] || vault[provider] === '#') - .map((provider) => ({ - type: 'input', - name: provider, - message: `Enter your ${provider.charAt(0).toUpperCase() + provider.slice(1)} API Key (Enter value, or press Enter to skip)\n`, - })); - - if (!_useSharedVault && missingKeyQuestions.length > 0) { + .map((provider) => { + if (hasDetectedKeys && detectedKeys[provider]) { + return { + type: 'confirm', + name: provider, + message: `We detected that ${provider} API Key is present in your environment variables. Do you want to use the environment variable in your project's vault?`, + default: true, + }; + } + return { + type: 'input', + name: provider, + message: `Enter your ${provider.charAt(0).toUpperCase() + provider.slice(1)} API Key (Enter value, or press Enter to skip)\n`, + }; + }); + + if (missingKeyQuestions.length > 0) { const keyAnswers = await inquirer.prompt(missingKeyQuestions); for (const [provider, key] of Object.entries(keyAnswers)) { if (key) { - vault[provider] = key as string; + vault[provider] = key === true ? detectedKeys[provider] : (key as string); } } } - console.log(chalk.yellowBright(`\n===[ Now let's configure Smyth Resources folder ]===`)); - console.log( - `${chalk.gray( - 'Some connectors in SmythOS might need to store data locally, in order to keep things clean, we store all SmythOS related data in a single place.\n' - )}` - ); - - const remainingAnswers = await inquirer.prompt([ - { - type: 'list', - name: 'smythResources', - message: 'Smyth Resources Folder', - suffix: `\n${chalk.magenta('Location where we can store data like logs, cache, etc. (Use arrow keys ↑↓)')}\n`, - choices: [ - { name: `Shared folder in the ${chalk.underline('user home directory')} (~/.smyth)`, value: path.join(os.homedir(), '.smyth') }, - { - name: `Local folder under ${chalk.underline('project root')} (./${path.basename(answers.targetFolder)}/.smyth)`, - value: path.join(answers.targetFolder, '.smyth'), - }, - ], - default: 0, - }, - ]); - - answers = { ...answers, ...remainingAnswers }; + answers = { ...answers, ...resourcesAnswers }; const finalConfig = { projectName: answers.projectName, @@ -404,21 +441,26 @@ async function createProject(config: any) { } //Write vault file - if (!config.useSharedVault) { - const vaultPath = path.join(config.smythResources, '.sre', 'vault.json'); - - //always create a default vault even if no key is configured - if (!fs.existsSync(vaultPath)) { - const vaultData = { - default: { - ...vaultTemplate.default, - ...config.vault, - }, - }; - fs.writeFileSync(vaultPath, JSON.stringify(vaultData, null, 2)); - } + //if (!config.useSharedVault) { + const vaultPath = path.join(config.smythResources, '.sre', 'vault.json'); + + //always create a default vault even if no key is configured + if (fs.existsSync(vaultPath)) { + //make a backup + const backupPath = path.join(config.smythResources, '.sre', 'vault.json.backup'); + fs.copyFileSync(vaultPath, backupPath); } + const vaultData = { + default: { + ...vaultTemplate.default, + ...config.vault, + }, + }; + fs.writeFileSync(vaultPath, JSON.stringify(vaultData, null, 2)); + + //} + //update package.json with project name const packageJsonPath = path.join(projectFolder, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); diff --git a/packages/core/docs/configuration.md b/packages/core/docs/configuration.md new file mode 100644 index 00000000..00ac2f1c --- /dev/null +++ b/packages/core/docs/configuration.md @@ -0,0 +1,86 @@ +# SRE Configuration + +SRE provides several ways to configure its behavior, including environment variables, command-line arguments, and settings files. This document outlines the available configuration methods and settings. + +## Configuration Layers + +Configuration is applied in the following order of precedence (higher numbers override lower numbers): + +1. **Default values:** Hardcoded defaults within the application. +2. **User settings folder (~/.smyth/):** Global settings for the current user's home directory. +3. **Project settings folder (.smyth/):** Project-specific settings. +4. **Environment variables:** System-wide or session-specific variables, which may be loaded from `.env` files. +5. **User Code:** User code can override configuration by passing a custom config to SRE and its connectors. + +## The .smyth Folder + +SRE uses `.smyth` folders to store configuration, secrets, and some persistent data used by connectors: + +### Location + +The `.smyth` folder can be located in two places: + +- **User settings folder:** + + - **Location:** `~/.smyth/` (where `~` is your home directory). + - **Scope:** Applies to all SRE sessions for the current user. + +- **Project settings folder:** + - **Location:** `.smyth/` within your project's root directory. + - **Scope:** Applies only when running SRE from that specific project. Project settings override user settings. + +### Content + +In this section, ".smyth" refers to either the user settings folder or the project settings folder, depending on the discovery order. + +#### .smyth/.sre/ Subfolder + +This subfolder contains the actual settings for SRE. + +- `.smyth/.sre/vault.json`: This file is present if you are using the default vault connector to manage API keys and secrets. It contains LLM API keys, other third-party API keys, and user-specified keys. It can be encrypted to protect the keys. (You can use other vault connectors to manage your secrets, such as AWS Secret Manager. In that case, this file will be ignored.) + +- `.smyth/.sre/config.json`: If present, this file defines the SRE startup configuration. + +#### Other Subfolders + +SRE can load connectors that need to store data locally. These connectors can be configured to store data in specific locations, but if no location is specified, they fall back to a default location, which is a subfolder of the `.smyth` folder. + +For example, the Local Storage connector uses the `.smyth/storage/` folder to store local data if no other location is specified. + +**Note on environment variables in settings:** String values within your `vault.json` or `config.json` files can reference environment variables using the `$env(VAR_NAME)` syntax. These variables will be automatically resolved at runtime. For example, if you have an environment variable `OPENAI_API_KEY`, you could use it in `vault.json` like this: `"openai": "$env(OPENAI_API_KEY)"`. + +### vault.json Structure + +The vault stores keys for different teams, but must have at least a "default" team configured. This is the team ID used for any agent that does not belong to a specific team. If an agent belongs to a specific team, it can only load secrets from that team's vault by default. The vault connector provides a configuration option to specify a shared vault entry that can be used as a fallback if a key is not found in the team's vault. This can be a completely separate entry (for example, called "shared"), or you can specify an existing entry (e.g., "default"). + +```json +{ + "default": { + "echo": "", + "openai": "sk-hardcoded-key", + "anthropic": "$env(ANTHROPIC_API_KEY)", + "googleai": "", + "groq": "", + "togetherai": "", + "tavily": "", + "my_custom_key": "1234567890" + }, + "team-id-0001": { + "echo": "...", + "openai": "...", + "anthropic": "...", + "googleai": "...", + "groq": "...", + "togetherai": "...", + "tavily": "...", + "my_custom_key": "..." + }, + "team-id-0002": { + //... + } +} +``` + +### config.json Structure + +(TBD) diff --git a/packages/core/package.json b/packages/core/package.json index 105caff1..21dc1e93 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@smythos/sre", - "version": "1.5.16", + "version": "1.5.20", "description": "Smyth Runtime Environment", "author": "Alaa-eddine KADDOURI", "license": "MIT", diff --git a/packages/sdk/docs/01-getting-started.md b/packages/sdk/docs/01-getting-started.md index 564cde61..00957fc5 100644 --- a/packages/sdk/docs/01-getting-started.md +++ b/packages/sdk/docs/01-getting-started.md @@ -6,10 +6,42 @@ The code in this guide is a more detailed version of the script found in [`examp ## 1. Installation -First, you need to install the SmythOS SDK. Since this is a `pnpm` workspace, we'll use that. Open your terminal and run: +### Method 1: Using the CLI (Recommended) + +The easiest way to get started is by using the SmythOS CLI to scaffold a new project: + +```bash +# Install the CLI globally +npm i -g @smythos/cli + +# Create a new project +sre create "My First Agent" +``` + +The CLI will guide you step-by-step to create your SDK project with the right configuration for your needs. Select **Empty Project** template when prompted. + +### Method 2: Direct SDK Installation + +If you prefer to add the SDK to an existing project, we recommend cloning one of our project templates: ```bash -pnpm install @smythos/sdk +# Clone a template (replace 'template-name' with your desired template) +git clone -b template-name https://github.com/SmythOS/sre-project-templates.git my-agent-project +cd my-agent-project +npm install +``` + +Available templates: + +- `empty-sdk-template` - Blank SDK project +- `minimal-sdk-agent` - Basic agent implementation +- `interactive-book-assistant` - Fully functional agent with interactive chat +- `interactive-chat-two-agents` - Two different agent implementations + +Alternatively, you can install the SDK directly in an existing project: + +```bash +npm install @smythos/sdk ``` The SDK is designed for a frictionless start. It automatically initializes the Smyth Runtime Environment (SRE) with in-memory components, so you can start building agents right away without any complex setup. @@ -69,7 +101,17 @@ main(); ## 3. Run Your Code -Save the file and run it from your terminal: +If you used the CLI to create your project, build and run it: + +```bash +# Build the project +npm run build + +# Run your agent +npm start +``` + +If you're using direct SDK installation, save the file and run it from your terminal: ```bash ts-node index.ts diff --git a/packages/sdk/package.json b/packages/sdk/package.json index c762638f..da2b1e91 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@smythos/sdk", - "version": "1.0.12", + "version": "1.0.20", "description": "SRE SDK", "keywords": [ "smythos",