commit 3720bd4c6d75414b31c11f409cac1caf4c0d01a5 Author: Zhongwei Li Date: Sat Nov 29 17:59:09 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..8d3e1ee --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "cloud", + "description": "Deploy Claude agents to cloud.atxp.ai", + "version": "1.1.1", + "author": { + "name": "ATXP Team", + "url": "https://atxp.ai" + }, + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..921473d --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# cloud + +Deploy Claude agents to cloud.atxp.ai diff --git a/commands/configure-env-var.md b/commands/configure-env-var.md new file mode 100644 index 0000000..4b2a480 --- /dev/null +++ b/commands/configure-env-var.md @@ -0,0 +1,168 @@ +--- +name: configure-env-var +description: Set an environment variable for your deployed agent +--- + +Sets an environment variable by writing it to `.atxp/.env.production`, which is included in deployments to cloud.atxp.ai. + +## Usage + +```bash +/configure-env-var KEY VALUE +``` + +Replace `KEY` with your environment variable name and `VALUE` with its value. + +## Examples + +Set API keys for external services: + +```bash +/configure-env-var GOOGLE_ANALYTICS_API_KEY ya29.a0AfH6SMBx... +/configure-env-var GOOGLE_SEARCH_CONSOLE_KEY AIzaSyC... +/configure-env-var DATAFORSEO_API_KEY 12345678-1234-1234-1234-123456789abc +``` + +## How it works + +1. Creates `.atxp/.env.production` if it doesn't exist +2. Adds or updates the environment variable in `KEY=VALUE` format +3. Sets file permissions to `600` (owner read/write only) +4. The file will be included in your next `/deploy` +5. Your deployed agent can access these variables at runtime + +## Requirements + +- Bash 3.2+ (default on macOS and most Linux distributions) +- Write access to current directory + +## Value Handling + +- **Empty values**: Not allowed. Use `/remove-env-var` to delete variables. +- **Values with spaces**: Fully supported (e.g., `"Hello World"`) +- **Values with special characters**: Supported including `=`, `:`, `/`, etc. +- **Multiline values**: Not supported. Use base64 encoding if needed. + +## Implementation + +```bash +#!/bin/bash + +# Parse arguments +if [ "$#" -lt 2 ]; then + echo "Usage: /configure-env-var KEY VALUE" + echo "Example: /configure-env-var API_KEY abc123" + exit 1 +fi + +KEY="$1" +shift +VALUE="$*" + +# Validate key format (alphanumeric and underscore only) +if ! echo "$KEY" | grep -qE '^[A-Za-z_][A-Za-z0-9_]*$'; then + echo "Error: KEY must start with a letter or underscore and contain only alphanumeric characters and underscores" + exit 1 +fi + +# Create .atxp directory if it doesn't exist +mkdir -p .atxp + +ENV_FILE=".atxp/.env.production" + +# Create or update the env file +if [ -f "$ENV_FILE" ]; then + # File exists, check if key already exists + if grep -q "^${KEY}=" "$ENV_FILE"; then + # Update existing key + # Use a temporary file for safety + TEMP_FILE=$(mktemp) || { + echo "Error: Failed to create temporary file" + exit 1 + } + while IFS= read -r line; do + if [[ "$line" =~ ^${KEY}= ]]; then + echo "${KEY}=${VALUE}" + else + echo "$line" + fi + done < "$ENV_FILE" > "$TEMP_FILE" + mv "$TEMP_FILE" "$ENV_FILE" + echo "✓ Updated ${KEY} in ${ENV_FILE}" + else + # Append new key + echo "${KEY}=${VALUE}" >> "$ENV_FILE" + echo "✓ Added ${KEY} to ${ENV_FILE}" + fi +else + # Create new file + echo "${KEY}=${VALUE}" > "$ENV_FILE" + chmod 600 "$ENV_FILE" + echo "✓ Created ${ENV_FILE} with ${KEY}" +fi + +# Ensure restrictive permissions on existing file +chmod 600 "$ENV_FILE" 2>/dev/null || true + +# Check if .gitignore exists and contains the env file +GITIGNORE_WARNED=false +if [ -f ".gitignore" ]; then + if ! grep -qF ".atxp/.env.production" .gitignore; then + GITIGNORE_WARNED=true + fi +else + GITIGNORE_WARNED=true +fi + +if [ "$GITIGNORE_WARNED" = true ]; then + echo "" + echo "⚠️ SECURITY WARNING: Add .atxp/.env.production to your .gitignore" + echo " Run: echo '.atxp/.env.production' >> .gitignore" +fi + +echo "" +echo "Next steps:" +echo " 1. Review variables: /list-env-vars" +echo " 2. Deploy your agent: /deploy" +``` + +## Security Considerations + +⚠️ **Important Security Notes:** + +1. **Git Protection**: The `.atxp/.env.production` file contains sensitive credentials and should never be committed to version control. Add it to your `.gitignore`: + ```bash + echo '.atxp/.env.production' >> .gitignore + ``` + The command will warn you if this entry is missing. + +2. **Shell History**: When using this command, secrets are stored in your shell history. For highly sensitive values, consider: + - Manually editing `.atxp/.env.production` instead + - Clearing your shell history after use: `history -d $(history 1)` + - Using environment variables: `/configure-env-var API_KEY "$SECRET_FROM_ENV"` + +3. **File Permissions**: The file is automatically created with `600` permissions (owner read/write only) to prevent other system users from accessing secrets. + +## File Format + +Variables are stored in standard `.env` format: + +``` +KEY1=value1 +KEY2=value2 +KEY3=value with spaces +``` + +## Notes + +- To update an existing variable, simply run the command again with the new value +- To view all configured variables, use `/list-env-vars` +- To remove a variable, use `/remove-env-var KEY` +- You can also manually edit `.atxp/.env.production` if needed +- Changes take effect on the next `/deploy` + +## Related Commands + +- `/list-env-vars` - List all configured environment variables +- `/remove-env-var` - Remove an environment variable +- `/deploy` - Deploy your agent with environment variables diff --git a/commands/deploy.md b/commands/deploy.md new file mode 100644 index 0000000..3a33981 --- /dev/null +++ b/commands/deploy.md @@ -0,0 +1,253 @@ +--- +name: deploy +description: Deploy the current working directory as a Claude agent to cloud.atxp.ai +--- + +# Deploy Command + +This command packages and deploys the current working directory to cloud.atxp.ai. + +## Prerequisites + +- `ATXP_CONNECTION_STRING` environment variable must be set with your ATXP connection URL +- The connection string should contain a `connection_token` query parameter +- `curl` and `jq` must be available in your PATH + +*IMPORTANT: When this command is complete, share the uploaded instance URL with the user! The URL is in this format: `https://cloud.atxp.ai/${instanceId}`.* + +## Steps + +### 1. Extract the connection token from ATXP_CONNECTION_STRING + +The connection token is a query parameter in the connection URL: + +```bash +# Extract connection_token from the URL +CONNECTION_TOKEN=$(echo "$ATXP_CONNECTION_STRING" | grep -o 'connection_token=[^&]*' | cut -d= -f2) + +if [ -z "$CONNECTION_TOKEN" ]; then + echo "Error: Could not extract connection_token from ATXP_CONNECTION_STRING" + exit 1 +fi +``` + +### 2. Create a zip file of the current directory + +```bash +# Create a temporary zip file (using mktemp for security) +TEMP_BASE=$(mktemp /tmp/deploy-XXXXXX) +ZIP_FILE="${TEMP_BASE}.zip" +rm "$TEMP_BASE" # Remove the temporary placeholder +echo "Creating deployment package..." + +# Zip the current directory, excluding common files and sensitive data +zip -r "$ZIP_FILE" . \ + -x ".git/*" \ + -x "node_modules/*" \ + -x "*/node_modules/*" \ + -x ".atxp-instance" \ + -x ".DS_Store" \ + -x "*/.DS_Store" \ + -x ".env.local" \ + -x ".env.*.local" \ + -x "*/.env.local" \ + -x "*/.env.*.local" \ + -x ".env.development" \ + -x "*/.env.development" \ + -x ".env.test" \ + -x "*/.env.test" \ + -x ".npmrc" \ + -x "*/.npmrc" \ + -x ".netrc" \ + -x "*/.netrc" \ + -x ".aws/*" \ + -x "*/.aws/*" \ + -x "*.pem" \ + -x "*.key" \ + -x "*.p12" \ + -x "*.pfx" \ + > /dev/null + +echo "Package created: $(du -h "$ZIP_FILE" | cut -f1)" +``` + +### 3. Check for existing instance ID + +The instance ID is stored in `.atxp-instance` in the current directory: + +```bash +INSTANCE_FILE=".atxp-instance" +INSTANCE_ID="" + +if [ -f "$INSTANCE_FILE" ]; then + INSTANCE_ID=$(cat "$INSTANCE_FILE") + echo "Found existing instance: $INSTANCE_ID" +fi +``` + +### 4. Upload to cloud.atxp.ai + +Create the authorization header using Basic Auth: + +```bash +# Create Basic Auth header: base64(TOKEN:) +AUTH_HEADER="Authorization: Basic $(echo -n "${CONNECTION_TOKEN}:" | base64)" +``` + +Upload the zip file (create new or update existing): + +```bash +if [ -z "$INSTANCE_ID" ]; then + # Create new instance + echo "Deploying new instance..." + RESPONSE=$(curl -X POST https://cloud.atxp.ai/upload \ + -H "$AUTH_HEADER" \ + -F "file=@$ZIP_FILE" \ + -s) +else + # Update existing instance + echo "Updating instance $INSTANCE_ID..." + RESPONSE=$(curl -X POST "https://cloud.atxp.ai/upload/$INSTANCE_ID" \ + -H "$AUTH_HEADER" \ + -F "file=@$ZIP_FILE" \ + -s) +fi + +# Clean up temporary zip file +rm "$ZIP_FILE" +``` + +### 5. Parse response and store instance ID + +```bash +# Check if upload was successful +SUCCESS=$(echo "$RESPONSE" | jq -r '.success') + +if [ "$SUCCESS" = "true" ]; then + INSTANCE_ID=$(echo "$RESPONSE" | jq -r '.instanceId') + MESSAGE=$(echo "$RESPONSE" | jq -r '.message') + + # Store instance ID for future deploys + echo "$INSTANCE_ID" > "$INSTANCE_FILE" + + echo "✓ Deployment successful!" + echo "Instance ID: $INSTANCE_ID" + echo "$MESSAGE" +else + echo "✗ Deployment failed" + echo "$RESPONSE" | jq -r '.message // .error // .' + exit 1 +fi +``` + +### 6. Remember to share the instance URL with the user! + +Again, the URL is in this format: `https://cloud.atxp.ai/${instanceId}`. + +## Complete Script + +Here's the complete deployment script: + +```bash +#!/bin/bash + +# Extract connection token +CONNECTION_TOKEN=$(echo "$ATXP_CONNECTION_STRING" | grep -o 'connection_token=[^&]*' | cut -d= -f2) + +if [ -z "$CONNECTION_TOKEN" ]; then + echo "Error: Could not extract connection_token from ATXP_CONNECTION_STRING" + exit 1 +fi + +# Create zip file (using mktemp for security) +TEMP_BASE=$(mktemp /tmp/deploy-XXXXXX) +ZIP_FILE="${TEMP_BASE}.zip" +rm "$TEMP_BASE" # Remove the temporary placeholder +echo "Creating deployment package..." +zip -r "$ZIP_FILE" . \ + -x ".git/*" \ + -x "node_modules/*" \ + -x "*/node_modules/*" \ + -x ".atxp-instance" \ + -x ".DS_Store" \ + -x "*/.DS_Store" \ + -x ".env.local" \ + -x ".env.*.local" \ + -x "*/.env.local" \ + -x "*/.env.*.local" \ + -x ".env.development" \ + -x "*/.env.development" \ + -x ".env.test" \ + -x "*/.env.test" \ + -x ".npmrc" \ + -x "*/.npmrc" \ + -x ".netrc" \ + -x "*/.netrc" \ + -x ".aws/*" \ + -x "*/.aws/*" \ + -x "*.pem" \ + -x "*.key" \ + -x "*.p12" \ + -x "*.pfx" \ + > /dev/null +echo "Package created: $(du -h "$ZIP_FILE" | cut -f1)" + +# Check for existing instance +INSTANCE_FILE=".atxp-instance" +INSTANCE_ID="" +if [ -f "$INSTANCE_FILE" ]; then + INSTANCE_ID=$(cat "$INSTANCE_FILE") + echo "Found existing instance: $INSTANCE_ID" +fi + +# Create auth header +AUTH_HEADER="Authorization: Basic $(echo -n "${CONNECTION_TOKEN}:" | base64)" + +# Upload +if [ -z "$INSTANCE_ID" ]; then + echo "Deploying new instance..." + RESPONSE=$(curl -X POST https://cloud.atxp.ai/upload \ + -H "$AUTH_HEADER" \ + -F "file=@$ZIP_FILE" \ + -s) +else + echo "Updating instance $INSTANCE_ID..." + RESPONSE=$(curl -X POST "https://cloud.atxp.ai/upload/$INSTANCE_ID" \ + -H "$AUTH_HEADER" \ + -F "file=@$ZIP_FILE" \ + -s) +fi + +# Clean up +rm "$ZIP_FILE" + +# Parse response +SUCCESS=$(echo "$RESPONSE" | jq -r '.success') + +if [ "$SUCCESS" = "true" ]; then + INSTANCE_ID=$(echo "$RESPONSE" | jq -r '.instanceId') + MESSAGE=$(echo "$RESPONSE" | jq -r '.message') + + echo "$INSTANCE_ID" > "$INSTANCE_FILE" + + echo "✓ Deployment successful!" + echo "Instance ID: $INSTANCE_ID" + echo "$MESSAGE" +else + echo "✗ Deployment failed" + echo "$RESPONSE" | jq -r '.message // .error // .' + exit 1 +fi +``` + +## Error Handling + +Possible errors: + +- **400 Bad Request**: Invalid file type or no file uploaded +- **401 Unauthorized**: Invalid or missing connection token +- **403 Forbidden**: Trying to update an instance you don't own +- **404 Not Found**: Instance ID not found (stored ID may be stale) +- **500 Internal Server Error**: Server error during upload + +If you receive a 404 error on update, delete the `.atxp-instance` file and try again to create a new instance. diff --git a/commands/list-env-vars.md b/commands/list-env-vars.md new file mode 100644 index 0000000..cac923d --- /dev/null +++ b/commands/list-env-vars.md @@ -0,0 +1,110 @@ +--- +name: list-env-vars +description: List configured environment variables +--- + +Lists all environment variables configured in `.atxp/.env.production` with their values masked for security. + +## Usage + +```bash +/list-env-vars +``` + +## Example Output + +``` +Environment variables in .atxp/.env.production: + + GOOGLE_ANALYTICS_API_KEY=ya29...**** + GOOGLE_SEARCH_CONSOLE_KEY=AIza...**** + DATAFORSEO_API_KEY=1234...**** + +(3 variables configured) +``` + +## Implementation + +```bash +#!/bin/bash + +ENV_FILE=".atxp/.env.production" + +if [ ! -f "$ENV_FILE" ]; then + echo "No environment variables configured yet." + echo "" + echo "To add variables, use:" + echo " /configure-env-var KEY VALUE" + exit 0 +fi + +# Count non-empty, non-comment lines +VAR_COUNT=$(grep -cE '^[A-Za-z_][A-Za-z0-9_]*=' "$ENV_FILE" 2>/dev/null || echo "0") + +if [ "$VAR_COUNT" -eq 0 ]; then + echo "No environment variables configured yet." + echo "" + echo "To add variables, use:" + echo " /configure-env-var KEY VALUE" + exit 0 +fi + +echo "Environment variables in .atxp/.env.production:" +echo "" + +# Read and display each variable with masked value +while IFS= read -r line; do + # Skip empty lines and comments + if [ -z "$line" ] || echo "$line" | grep -qE '^\s*#'; then + continue + fi + + # Extract key and mask value + if echo "$line" | grep -qE '^[A-Za-z_][A-Za-z0-9_]*='; then + KEY=$(echo "$line" | cut -d= -f1) + VALUE=$(echo "$line" | cut -d= -f2-) + + # Mask the value - show first 4 chars and **** + if [ ${#VALUE} -le 4 ]; then + MASKED="****" + else + FIRST_CHARS=$(echo "$VALUE" | cut -c1-4) + MASKED="${FIRST_CHARS}...****" + fi + + echo " ${KEY}=${MASKED}" + fi +done < "$ENV_FILE" + +echo "" +echo "(${VAR_COUNT} variable(s) configured)" +echo "" +echo "To modify or add variables, use:" +echo " /configure-env-var KEY VALUE" +echo "" +echo "To remove a variable, use:" +echo " /remove-env-var KEY" +``` + +## Notes + +- Variable values are masked to prevent accidental exposure +- Only shows the first 4 characters followed by `...****` +- Empty lines and comments are ignored +- To see actual values, view the `.atxp/.env.production` file directly + +## Security + +This command masks sensitive values by default. If you need to see the actual values for debugging, you can: + +```bash +cat .atxp/.env.production +``` + +⚠️ Be careful when viewing or sharing the actual file contents. + +## Related Commands + +- `/configure-env-var` - Add or update an environment variable +- `/remove-env-var` - Remove an environment variable +- `/deploy` - Deploy your agent with environment variables diff --git a/commands/remove-env-var.md b/commands/remove-env-var.md new file mode 100644 index 0000000..af5cfef --- /dev/null +++ b/commands/remove-env-var.md @@ -0,0 +1,132 @@ +--- +name: remove-env-var +description: Remove an environment variable +--- + +Removes an environment variable from `.atxp/.env.production`. + +## Usage + +```bash +/remove-env-var KEY +``` + +Replace `KEY` with the name of the environment variable you want to remove. + +## Examples + +Remove a single variable: + +```bash +/remove-env-var GOOGLE_ANALYTICS_API_KEY +``` + +Remove multiple variables: + +```bash +/remove-env-var DATAFORSEO_API_KEY +/remove-env-var OLD_API_KEY +``` + +## Implementation + +```bash +#!/bin/bash + +# Parse arguments +if [ "$#" -ne 1 ]; then + echo "Usage: /remove-env-var KEY" + echo "Example: /remove-env-var API_KEY" + exit 1 +fi + +KEY="$1" + +ENV_FILE=".atxp/.env.production" + +if [ ! -f "$ENV_FILE" ]; then + echo "No environment variables file found at ${ENV_FILE}" + echo "" + echo "Nothing to remove." + exit 0 +fi + +# Check if the key exists +if ! grep -q "^${KEY}=" "$ENV_FILE"; then + echo "Environment variable ${KEY} not found in ${ENV_FILE}" + echo "" + echo "To see all configured variables, use:" + echo " /list-env-vars" + exit 1 +fi + +# Create a temporary file and filter out the key +TEMP_FILE=$(mktemp) || { + echo "Error: Failed to create temporary file" + exit 1 +} +grep -v "^${KEY}=" "$ENV_FILE" > "$TEMP_FILE" || true + +# Replace the original file +mv "$TEMP_FILE" "$ENV_FILE" + +echo "✓ Removed ${KEY} from ${ENV_FILE}" + +# Check if file is now empty +if [ ! -s "$ENV_FILE" ]; then + echo "" + echo "The environment variables file is now empty." + rm "$ENV_FILE" + echo "✓ Removed empty ${ENV_FILE}" +fi + +echo "" +echo "Next steps:" +echo " 1. Review remaining variables: /list-env-vars" +echo " 2. Deploy your agent: /deploy" +``` + +## How it works + +1. Searches for the environment variable in `.atxp/.env.production` +2. Removes the line containing that variable +3. If the file becomes empty after removal, deletes the file +4. Changes take effect on the next `/deploy` + +## Notes + +- The variable name is case-sensitive +- To see all configured variables before removing, use `/list-env-vars` +- If you remove all variables, the `.atxp/.env.production` file will be deleted +- Changes only take effect after running `/deploy` +- You can also manually edit `.atxp/.env.production` if needed + +## Example Workflow + +```bash +# List current variables +/list-env-vars +# Output: +# GOOGLE_ANALYTICS_API_KEY=ya29...**** +# OLD_API_KEY=test...**** +# (2 variables configured) + +# Remove the old key +/remove-env-var OLD_API_KEY +# Output: ✓ Removed OLD_API_KEY from .atxp/.env.production + +# Verify removal +/list-env-vars +# Output: +# GOOGLE_ANALYTICS_API_KEY=ya29...**** +# (1 variable configured) + +# Deploy with updated variables +/deploy +``` + +## Related Commands + +- `/configure-env-var` - Add or update an environment variable +- `/list-env-vars` - List all configured environment variables +- `/deploy` - Deploy your agent with environment variables diff --git a/commands/setup.md b/commands/setup.md new file mode 100644 index 0000000..7c60f79 --- /dev/null +++ b/commands/setup.md @@ -0,0 +1,18 @@ +--- +name: setup +description: Save the user's ATXP connection string to the current shell environment +--- + +Run the setup to set $ATXP_CONNECTION_STRING environment variable. + +Usage: + +``` +/setup +``` + +This should result in setting the $ATXP_CONNECTION_STRING environment variable to the user's connection string. + +After running this command, the $ATXP_CONNECTION_STRING environment variable will contain the user's connection string. + +Please let the user know if their connection string was saved to the shell environment successfully. \ No newline at end of file diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..55886a5 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,61 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:atxp-dev/claude:cloud", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "2d7db30e30a19c73eb901353210253974602c7ad", + "treeHash": "9950d02dce0efa8981a33d25f0a2fff10c2f920ce276c809e5484a3482cb7d9d", + "generatedAt": "2025-11-28T10:14:01.478640Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "cloud", + "description": "Deploy Claude agents to cloud.atxp.ai", + "version": "1.1.1" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "55df3bcfc87381f48dfa5774917e872fe2a48f0b597deef9b654dec3c0f5c1ac" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "443944906b55ae783c152a52362e15102b77ff439d3f2355ad4179a3c4b39a12" + }, + { + "path": "commands/list-env-vars.md", + "sha256": "c077b5c92b88712c7d6e3a035f8b7a244da7a39105e0482ed1849d2c614561e7" + }, + { + "path": "commands/setup.md", + "sha256": "3bdab2a064a86a1562fd1f498b09c0196bf48cec7bad00e74348c5d8aa3793a8" + }, + { + "path": "commands/deploy.md", + "sha256": "86a08b86d93dd258477807c5041e70ee7e7f03a3412300a588c89ab2c40a2cbb" + }, + { + "path": "commands/configure-env-var.md", + "sha256": "c234e86e706e25dae23d3057e8a4b530bdbe0e4980baa8bad5f590f6be988f80" + }, + { + "path": "commands/remove-env-var.md", + "sha256": "3f08df42497e46405b5670b8e8185f473e50521f155c55f086638410f96f0f2b" + } + ], + "dirSha256": "9950d02dce0efa8981a33d25f0a2fff10c2f920ce276c809e5484a3482cb7d9d" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file