@microfox/cli
Version:
Universal CLI tool for creating modern TypeScript packages with npm availability checking
486 lines (454 loc) • 11.9 kB
Plain Text
--- filename: package.json
{
"name": "<%= agentName %>",
"version": "1.0.0",
"description": "A Microbizz agent.",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "tsc",
"deploy": "npm run build && serverless deploy --stage dev",
"deploy:preview": "npm run build && serverless deploy --stage preview",
"deploy:prod": "npm run build && serverless deploy --stage prod",
"remove": "serverless remove",
"remove:preview": "serverless remove --stage preview",
"remove:prod": "serverless remove --stage prod",
"start": "serverless offline start",
"dev": "npm run build && serverless offline start",
"lint": "eslint ."
},
"dependencies": {
"@aws-sdk/client-sqs": "^3.504.0",
"@upstash/redis": "^1.28.4",
"ai": "latest",
"aws-lambda": "*",
"dotenv": "^16.4.5",
"uuid": "^9.0.1"
},
"devDependencies": {
"@eslint/js": "latest",
"@types/aws-lambda": "^8.10.138",
"@types/node": "^20.11.19",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "latest",
"@typescript-eslint/parser": "latest",
"eslint": "^9.1.0",
"serverless": "^3.38.0",
"serverless-offline": "^13.3.3",
"serverless-plugin-typescript": "^2.1.5",
"typescript": "^5.3.3"
}
}
--- filename: serverless.yml
service: <%= agentName %>
package:
exclude:
- venv/**
- .idea/**
- .vscode/**
- src/**
- node_modules/serverless-offline/**
custom:
stage: ${env:ENVIRONMENT, 'dev'}
serverless-offline:
httpPort: 4000
lambdaPort: 4002
useChildProcesses: true
useWorkerThreads: true
noCookieValidation: true
allowCache: true
hideStackTraces: false
disableCookieValidation: true
noTimeout: true
environment: ${file(env.json)}
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
stage: ${env:ENVIRONMENT, 'dev'}
environment: ${file(env.json)}
iam:
role:
statements:
- Effect: 'Allow'
Action:
- 'sqs:SendMessage'
Resource:
- Fn::GetAtt: [MessageQueue, Arn]
plugins:
- serverless-plugin-typescript
- serverless-offline
functions:
# Example HTTP Function
handleApiRequest:
handler: dist/functions/api-handler.handler
events:
- http:
path: /items
method: POST
cors: true
# Example Cron Job
handleScheduledTask:
handler: dist/functions/cron-handler.handler
events:
- schedule: rate(1 day)
# Example SQS Handler
handleQueueMessage:
handler: dist/functions/sqs-handler.handler
events:
- sqs:
arn:
Fn::GetAtt:
- MessageQueue
- Arn
# Documentation
getDocs:
handler: dist/index.getDocs
events:
- http:
path: /docs.json
method: get
cors: true
resources:
Resources:
MessageQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: ${self:service}-message-queue-${opt:stage,env:ENVIRONMENT, 'dev'}
VisibilityTimeout: 300
Outputs:
ApiEndpoints:
Description: 'API Endpoints'
Value:
Fn::Join:
- ''
- - 'API: https://'
- Ref: 'ApiGatewayRestApi'
- '.execute-api.'
- Ref: 'AWS::Region'
- ".amazonaws.com/${env:ENVIRONMENT, 'dev'}"
--- filename: tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2020"],
"allowJs": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false,
"noImplicitAny": true,
"noCheck": false,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": "./src",
"paths": {
"@/*": ["*"]
}
},
"ts-node": {
"esm": true,
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext"
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist/**", ".serverless/**"]
}
--- filename: eslint.config.js
import js from '@eslint/js';
import tseslint from '@typescript-eslint/eslint-plugin';
import tsparser from '@typescript-eslint/parser';
export default [
js.configs.recommended,
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
globals: {
console: 'readonly',
process: 'readonly',
},
},
plugins: {
'@typescript-eslint': tseslint,
},
rules: {
...tseslint.configs.recommended.rules,
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['warn'],
'no-console': 'warn',
'react/prop-types': 'off',
'no-case-declarations': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
},
},
{
ignores: [
'dist/**',
'node_modules/**',
'.turbo/**',
'coverage/**',
'**/*.js',
'**/*.test.ts',
],
},
];
--- filename: env.json
{
"ENVIRONMENT": "dev",
"UPSTASH_REDIS_REST_URL": "your_redis_url",
"UPSTASH_REDIS_REST_TOKEN": "your_redis_token"
}
--- filename: microfox.json
{
"stage": "prod"
}
--- filename: README.md
# <%= agentName %> Agent
This is a Microbizz agent for <%= agentName %>.
## Getting Started
### Prerequisites
- Node.js >= 20.x
- Serverless Framework
- AWS Account
### Installation
1. Clone the repository
2. Install dependencies:
```bash
npm install
```
3. Configure your environment variables in `env.json`.
### Running locally
```bash
npm run dev
```
### Deployment
```bash
# Deploy to development
npm run deploy
# Deploy to production
npm run deploy:prod
```
--- filename: .gitignore
# Dependencies
node_modules/
dist/
.serverless/
.env
env.json
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# IDE
.idea/
.vscode/
# OS
.DS_Store
# Logs
logs
*.log
--- filename: src/index.ts
import { handleApiRequestDocs } from './functions/api-handler.js'
import { handleScheduledTaskDocs } from './functions/cron-handler.js'
import { handleQueueMessageDocs } from './functions/sqs-handler.js'
/**
* Complete API documentation
* This is an OpenAPI 3.0.1 definition for the agent's API.
*/
const apiDocs = {
openapi: '3.0.1',
info: {
title: '<%= agentName %> API',
version: '1.0.0',
description: 'API for the <%= agentName %> agent.',
contact: {
name: 'API Support',
email: 'support@microfox.app',
},
},
servers: [
{
url: 'https://api.microfox.com/c/some-hash',
description: 'Production server',
},
],
paths: {
'/items': {
post: handleApiRequestDocs,
},
},
components: {
schemas: {
//...handleApiRequestDocs.components.schemas
}
}
}
/**
* GET endpoint to serve API documentation.
* This function returns the OpenAPI specification in JSON format.
*/
export const getDocs = async () => {
try {
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify(apiDocs, null, 2),
}
} catch (error) {
console.error('Error serving docs:', error)
return {
statusCode: 500,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
},
body: JSON.stringify({
error: error instanceof Error ? error.message : 'Unknown error',
}),
}
}
}
--- filename: src/helpers/redis.ts
import { Redis } from '@upstash/redis'
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL || '',
token: process.env.UPSTASH_REDIS_REST_TOKEN || '',
})
export default redis;
// You can add your helper functions for redis here
--- filename: src/functions/api-handler.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import redis from '../helpers/redis.js';
export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
if (!event.body) {
return {
statusCode: 400,
body: JSON.stringify({ message: 'Missing request body' }),
};
}
try {
const { itemId } = JSON.parse(event.body);
if (!itemId) {
return {
statusCode: 400,
body: JSON.stringify({ message: 'Missing itemId in request body' }),
};
}
const item = await redis.get(`item:${itemId}`);
if (!item) {
return {
statusCode: 404,
body: JSON.stringify({ message: 'Item not found' }),
};
}
return {
statusCode: 200,
body: JSON.stringify(item),
};
} catch (error) {
console.error(error);
if (error instanceof SyntaxError) {
return {
statusCode: 400,
body: JSON.stringify({ message: 'Invalid JSON in request body' }),
};
}
return {
statusCode: 500,
body: JSON.stringify({ message: 'Internal server error' }),
};
}
};
export const handleApiRequestDocs = {
summary: 'Retrieve an item by ID',
description: 'Retrieves a specific item from the database using its ID.',
tags: ['Items'],
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
properties: {
itemId: {
type: 'string',
description: 'The ID of the item to retrieve.'
}
},
required: ['itemId']
}
}
}
},
responses: {
'200': {
description: 'Successful response with the item.',
content: {
'application/json': {
schema: {
type: 'object',
properties: {
id: { type: 'string' },
data: { type: 'string' }
}
}
}
}
},
'400': { description: 'Bad Request: Missing or invalid request body.' },
'404': { description: 'Item not found.' },
'500': { description: 'Internal server error.' },
},
};
--- filename: src/functions/cron-handler.ts
export const handler = async () => {
console.log('Running scheduled task...');
// Add your logic for the scheduled task here.
// For example, processing data, generating reports, etc.
console.log('Scheduled task finished.');
};
export const handleScheduledTaskDocs = {
summary: 'Scheduled Task',
description: 'This function runs on a schedule to perform routine tasks.',
tags: ['Cron Jobs'],
};
--- filename: src/functions/sqs-handler.ts
import { SQSHandler, SQSEvent } from 'aws-lambda';
export const handler: SQSHandler = async (event: SQSEvent) => {
for (const record of event.Records) {
try {
const body = JSON.parse(record.body);
console.log('Processing message:', body);
// Add your SQS message processing logic here
} catch (error) {
console.error('Error processing message:', error);
// Decide if you want to re-queue the message, or send to a DLQ
}
}
};
export const handleQueueMessageDocs = {
summary: 'Process SQS Queue',
description: 'This function processes messages from an SQS queue.',
tags: ['SQS Handlers'],
};