
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@openpets/slack
Advanced tools
Slack integration plugin for OpenCode - search channels, read messages, and post to Slack
A comprehensive template for creating OpenCode plugins using the factory pattern with multi-provider workflow management. This template demonstrates modern plugin architecture with provider abstraction, configuration management, and workflow orchestration.
This template uses the factory pattern from src/core/plugin-factory.ts:
createPlugin() and createSingleTool()✅ Type Safety: Zod schemas ensure parameter validation
✅ Provider Agnostic: Support multiple providers per service type
✅ Graceful Fallbacks: Automatic provider switching on failures
✅ Configuration Driven: Environment-based provider selection
✅ Workflow Focused: High-level orchestration tools
interface PluginConfig {
providers: {
github?: { token: string; apiUrl?: string }
maps?: { provider: 'googlemaps' | 'mapbox'; apiKey: string }
ai?: { provider: 'fal'; apiKey: string }
}
preferences: Record<string, string>
workflow: { timeout: number; retries: number; logLevel: string }
}
PROVIDER_PREFERENCES JSON# Navigate to pets directory
cd /Users/andrewmaguire/LOCAL/Github/raggle-code/raggle-repo/core/pets/
# Copy template
cp -r _TEMPLATE_ my-workflow-manager
cd my-workflow-manager
npm install
# Copy environment template
cp .env.example .env
# Edit with your API keys
nano .env
Example .env:
# Primary providers
GITHUB_TOKEN=ghp_your_github_token
GOOGLE_MAPS_API_KEY=your_google_maps_key
FAL_KEY=your_fal_api_key
# Provider preferences
PROVIDER_PREFERENCES={"maps":"googlemaps","ai":"fal"}
# Workflow settings
DEFAULT_TIMEOUT=30000
MAX_RETRIES=3
LOG_LEVEL=info
# Check provider status
opencode run "check provider status"
# Demo workflow
opencode run "demo workflow with geocode and enhance for 'New York'"
| Variable | Description | Required |
|---|---|---|
GITHUB_TOKEN | GitHub personal access token | Optional |
GOOGLE_MAPS_API_KEY | Google Maps API key | Optional |
MAPBOX_ACCESS_TOKEN | Mapbox access token | Optional |
FAL_KEY | FAL AI API key | Optional |
PROVIDER_PREFERENCES | JSON string with provider preferences | Optional |
DEFAULT_TIMEOUT | Operation timeout in milliseconds | Optional |
MAX_RETRIES | Maximum retry attempts | Optional |
LOG_LEVEL | Logging level (debug/info/warn/error) | Optional |
{
"maps": "googlemaps",
"ai": "fal",
"version_control": "github"
}
Demonstrates multi-provider workflow orchestration.
Actions:
geocode-and-enhance: Geocode location and enhance with AIcreate-and-tag: Create repository with location-based tagsai-process: Process text using AI providerExamples:
opencode run "demo workflow with geocode and enhance for 'San Francisco'"
opencode run "demo workflow with create and tag for 'my-project'"
opencode run "demo workflow with ai process for 'Hello world'"
Check provider status and availability.
Actions:
status: Show all provider statusestest: Test provider connectivitylist: List available and configured providersExamples:
opencode run "check provider status"
opencode run "test all providers"
opencode run "list available providers"
Manage plugin configuration.
Actions:
get: Get configuration valuesset: Set configuration valuesvalidate: Validate configurationExamples:
opencode run "get configuration"
opencode run "validate plugin configuration"
opencode run "set configuration key 'test.value' to 'demo'"
# Create new provider in src/utils/
mkdir src/utils/my-provider
cd src/utils/my-provider
// src/utils/my-provider/index.ts
export interface MyProviderConfig {
apiKey: string
apiUrl?: string
}
export class MyProviderAPI {
constructor(private config: MyProviderConfig) {}
async processData(data: string) {
// Implementation
}
}
interface PluginConfig {
providers: {
// Add new provider
myprovider?: { apiKey: string; apiUrl?: string }
}
}
const initializeProviders = async (config: PluginConfig) => {
const providers: any = {}
if (config.providers.myprovider) {
const { MyProviderAPI } = await import('../../src/utils/my-provider')
providers.myprovider = new MyProviderAPI(config.providers.myprovider)
}
return providers
}
// ✅ Good: Use factory for tool creation
const tools = [
{
name: "my-tool",
description: "Tool description",
schema: z.object({ /* ... */ }),
execute: async (args) => { /* ... */ }
}
]
return createPlugin(tools)
// ❌ Bad: Direct tool definition
return {
tool: {
"my-tool": tool({ /* ... */ })
}
}
// ✅ Good: Lazy loading with error handling
const initializeProvider = async (config: any) => {
try {
const ProviderAPI = await import('../../src/utils/provider')
return new ProviderAPI(config)
} catch (error) {
console.warn(`Provider initialization failed:`, error)
return null
}
}
// ❌ Bad: Eager loading without error handling
const provider = new ProviderAPI(config) // Fails if import errors
// ✅ Good: Comprehensive validation
const validateConfig = (config: PluginConfig) => {
const errors: string[] = []
if (!config.providers.github && !config.providers.maps) {
errors.push("At least one provider must be configured")
}
return { valid: errors.length === 0, errors }
}
// ❌ Bad: No validation
const providers = await initializeProviders(config) // May fail silently
// ✅ Good: Structured error responses
return createResponse(false, undefined, [{
error: error.message,
provider: providerName,
action: args.action
}], { duration: Date.now() - startTime })
// ❌ Bad: Unstructured errors
throw new Error(`Operation failed: ${error.message}`)
Old Structure:
export const LegacyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
return {
tool: {
"tool-name": tool({
description: "...",
args: { /* ... */ },
execute: async (args) => { /* ... */ }
})
}
}
}
New Factory Structure:
export const FactoryPlugin = async () => {
const config = loadConfiguration()
const providers = await initializeProviders(config)
const tools = createWorkflowTools(providers, config)
return createPlugin(tools)
}
{ project, client, $, directory, worktree }loadConfiguration() functioninitializeProviders() functioncreatePlugin() instead of direct tool objects# Test individual tools
npm run test:queries
# Test workflow scenarios
npm run test:scenarios
# Test all functionality
npm run test:all
# Test provider connectivity
opencode run "test all providers"
# Test provider fallbacks
# Temporarily disable primary provider and test fallbacks
# Test with missing environment variables
unset GITHUB_TOKEN
opencode run "validate plugin configuration"
# Test with invalid configuration
PROVIDER_PREFERENCES='{"invalid":"provider"}' opencode run "check provider status"
const discoverProviders = async () => {
const providerDir = '../../src/utils'
const providers = await fs.readdir(providerDir)
return Promise.all(
providers
.filter(name => !name.startsWith('.'))
.map(async name => {
try {
const module = await import(`${providerDir}/${name}`)
return { name, module: module.default || module }
} catch {
return { name, error: 'Failed to load' }
}
})
)
}
const monitorProviderHealth = async (providers: any) => {
const health = await Promise.allSettled(
Object.entries(providers).map(async ([name, provider]) => [
name,
{
status: await testProvider(provider),
lastCheck: new Date().toISOString(),
responseTime: await measureResponseTime(provider)
}
])
)
return Object.fromEntries(health)
}
const composeWorkflow = (steps: WorkflowStep[]) => async (input: any) => {
let result = input
for (const step of steps) {
try {
result = await step.execute(result)
} catch (error) {
if (step.required) {
throw error
}
console.warn(`Optional step ${step.name} failed:`, error)
}
}
return result
}
my-workflow-manager/
├── index.ts # Factory-based plugin implementation
├── package.json # Dependencies and metadata
├── README.md # This documentation
├── .env.example # Environment variable template
├── .env # Your actual configuration (gitignored)
├── .gitignore # Git ignore file
└── opencode.json # OpenCode configuration
src/core/plugin-factory.tssrc/utils/ directoryprompts/pet-creator.md for LLM guidanceMIT - feel free to use this template for your own workflow managers!
Happy workflow building! 🚀
FAQs
Slack integration plugin for OpenCode - search channels, read messages, and post to Slack
We found that @openpets/slack demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.