diff --git a/agents.md b/AGENTS.md similarity index 100% rename from agents.md rename to AGENTS.md diff --git a/VERSION b/VERSION index ee6cdce..b616048 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.1 +0.6.2 diff --git a/cmd/localforge/api_config/cloudinary.json b/cmd/localforge/api_config/cloudinary.json new file mode 100644 index 0000000..31fb636 --- /dev/null +++ b/cmd/localforge/api_config/cloudinary.json @@ -0,0 +1,37 @@ +{ + "serviceName": "Cloudinary", + "serviceDescription": "Image upload and CDN management. Upload images, list/delete resources, get secure URLs for Instagram and other integrations.", + "basic_auth": "${CLOUDINARY_API_KEY}:${CLOUDINARY_API_SECRET}", + "endpoints": [ + { + "name": "upload_image", + "url": "https://api.cloudinary.com/v1_1/${CLOUDINARY_CLOUD_NAME}/image/upload", + "method": "POST", + "content_type": "form", + "description": "Upload an image to Cloudinary using Basic Auth (API Key + Secret). Returns secure_url — the public HTTPS URL to use with Instagram.", + "payload": "resolve_to_base64_$file: local file path to upload (e.g. /home/user/photo.jpg). Required.\npublic_id: string - Optional identifier for the asset (used in delivery URLs).\nfolder: string - Optional folder path to organise assets (e.g. \"instagram/posts\").\ntags: string - Optional comma-separated tags." + }, + { + "name": "get_resource", + "url": "https://api.cloudinary.com/v1_1/${CLOUDINARY_CLOUD_NAME}/resources/image/upload/{public_id}", + "method": "GET", + "description": "Get details of an uploaded image by its public ID, including its secure_url.", + "url_parameters": "public_id: string - The public ID of the image." + }, + { + "name": "list_resources", + "url": "https://api.cloudinary.com/v1_1/${CLOUDINARY_CLOUD_NAME}/resources/image/upload", + "method": "GET", + "description": "List uploaded images in the Cloudinary account.", + "query_params": "max_results: integer - Maximum number of resources to return (default: 10, max: 500).\nnext_cursor: string - Pagination cursor from a previous response." + }, + { + "name": "delete_resource", + "url": "https://api.cloudinary.com/v1_1/${CLOUDINARY_CLOUD_NAME}/image/destroy", + "method": "POST", + "content_type": "form", + "description": "Delete an uploaded image by its public ID.", + "payload": "public_id: string - The public ID of the image to delete. Required.\ninvalidate: boolean - Whether to invalidate the CDN cache (default: false)." + } + ] +} diff --git a/src/builder/tools.go b/src/builder/tools.go index 526014b..a0f7ea8 100644 --- a/src/builder/tools.go +++ b/src/builder/tools.go @@ -109,7 +109,7 @@ func (t *Tool) getTool( if !filepath.IsAbs(folderPath) { folderPath = filepath.Join(workingDir, folderPath) } - repositoryDir := filepath.Join(workingDir, "repository", "api_configs") + repositoryDir := filepath.Join(workingDir, "api_config") _ = os.MkdirAll(repositoryDir, 0755) services := make(map[string]api.ServiceConfig) diff --git a/src/plugins/procedures/plugin.go b/src/plugins/procedures/plugin.go index b013657..dcd00a6 100644 --- a/src/plugins/procedures/plugin.go +++ b/src/plugins/procedures/plugin.go @@ -47,7 +47,7 @@ type Procedure struct { type ProceduresPlugin struct { // dir is the directory for user-created procedures (workingDir/procedures). dir string - // repositoryDir is the directory for remotely installed procedures (workingDir/repository/procedures). + // repositoryDir is the directory for remotely installed procedures (workingDir/procedures). repositoryDir string procedures map[string]*Procedure activeProcedure *Procedure @@ -55,11 +55,10 @@ type ProceduresPlugin struct { } // NewProceduresPlugin creates a new ProceduresPlugin. -// User procedures live in workingDir/procedures. -// Repository-installed procedures live in workingDir/repository/procedures. +// User and repository-installed procedures both live in workingDir/procedures. func NewProceduresPlugin(workingDir string) *ProceduresPlugin { dir := filepath.Join(workingDir, "procedures") - repositoryDir := filepath.Join(workingDir, "repository", "procedures") + repositoryDir := filepath.Join(workingDir, "procedures") _ = os.MkdirAll(dir, 0755) _ = os.MkdirAll(repositoryDir, 0755) p := &ProceduresPlugin{ @@ -104,7 +103,7 @@ func (p *ProceduresPlugin) SystemPrompt() string { sb.WriteString("[PROCEDURES]\n") sb.WriteString("- Tool: procedure\n") sb.WriteString("- Structured multi-step tasks. Actions: start_procedure, next_step, goto_step (jump to step by number).\n") - sb.WriteString("- User-created procedures live in procedures/. Repository-installed procedures live in repository/procedures/. When creating procedures, always use paths under procedures/ (e.g. procedures/my-procedure/).\n\n") + sb.WriteString("- User-created and repository-installed procedures live in procedures/. When creating procedures, always use paths under procedures/ (e.g. procedures/my-procedure/).\n\n") sb.WriteString("[PROCEDURE EXECUTION RULE — MANDATORY]\n") sb.WriteString("At ANY step or tool call, if the outcome is not exactly what the step describes, or a Tool returne any error or unexpected result as expected:\n") sb.WriteString("1. STOP immediately. Do not continue, retry, guess, or attempt to work around the problem.\n") diff --git a/src/plugins/procedures/tools.go b/src/plugins/procedures/tools.go index defd239..87703cf 100644 --- a/src/plugins/procedures/tools.go +++ b/src/plugins/procedures/tools.go @@ -13,13 +13,13 @@ const PROCEDURE_TOOL = "procedure" func newProcedureTool(plugin *ProceduresPlugin) llms.Tool { return &core.Tool{ Name: PROCEDURE_TOOL, - Description: `Execute structured multi-step procedures. start_procedure: begin named procedure (requires name). next_step: advance to next step. goto_step: jump to a specific step (requires stepNumber). installable_procedures: list procedures available on GitHub. install_procedure: download a procedure from GitHub into repository/procedures/ (requires procedureSlug).`, + Description: `Execute structured multi-step procedures. start_procedure: begin named procedure (requires name). next_step: advance to next step. goto_step: jump to a specific step (requires stepNumber). installable_procedures: list procedures available on GitHub. install_procedure: download a procedure from GitHub into procedures/ (requires procedureSlug).`, AdvanceDesc: `[ACTIONS] - start_procedure: Begin from step 0. Required: name - next_step: Advance to next step of active procedure - goto_step: Jump to a specific step by number. Required: stepNumber (0-based) - installable_procedures: List all procedures available for installation from the remote GitHub repository -- install_procedure: Download a procedure from GitHub into the local repository/procedures/ folder. Required: procedureSlug (directory name in the remote repo, e.g. 'gmail-login') +- install_procedure: Download a procedure from GitHub into the local procedures/ folder. Required: procedureSlug (directory name in the remote repo, e.g. 'gmail-login') [STEP CONTENT] - Each action returns step folder files. Read for instructions. @@ -27,16 +27,16 @@ func newProcedureTool(plugin *ProceduresPlugin) llms.Tool { [CREATING PROCEDURES] - New procedures MUST be created inside procedures/ (e.g. procedures/my-procedure/manifest.yaml, procedures/my-procedure/0/instructions.md). Never create at working dir root. -- Procedures installed from the repository are stored in repository/procedures/ and must not be edited directly.`, +- Procedures installed from the repository are stored in procedures/ alongside user-created procedures.`, TroubleshootingInfo: `Troubleshooting: - Ensure 'action' is 'start_procedure', 'next_step', 'goto_step', 'installable_procedures', or 'install_procedure'. - 'name' is required for start_procedure and must match a known procedure name exactly. - 'stepNumber' is required for goto_step (0-based index). -- 'procedureSlug' is required for install_procedure and must match a directory name in the remote repo's repository/procedures/ folder. +- 'procedureSlug' is required for install_procedure and must match a directory name in the remote repo's procedures folder. - Call start_procedure before next_step or goto_step; there is no active procedure otherwise. - next_step returns an error when the last step has already been reached. - install_procedure requires internet access to reach the GitHub API. -- Installed procedures land in repository/procedures/, not procedures/.`, +- Installed procedures land in procedures/.`, Parameters: []core.Parameter{ { Name: "action", diff --git a/src/tools/api/README.md b/src/tools/api/README.md index f32f7b9..298bb04 100644 --- a/src/tools/api/README.md +++ b/src/tools/api/README.md @@ -250,7 +250,7 @@ services := map[string]api.ServiceConfig{ }, } -repositoryDir := "" // path to repository/api_configs for install_api_config; empty disables +repositoryDir := "" // path to api_config for install_api_config (workingDir/api_config when using builder); empty disables workingDir := "/path/to/agent/working/dir" // base path for resolving relative file paths in resolvers tool := api.NewApiTool("api", services, repositoryDir, workingDir) ``` diff --git a/src/tools/api/tool.go b/src/tools/api/tool.go index dec4246..c66336b 100644 --- a/src/tools/api/tool.go +++ b/src/tools/api/tool.go @@ -30,7 +30,7 @@ func (w *apiToolWrapper) ServiceNames() []string { // NewApiTool creates a unified API tool from a map of service configurations. // name is the tool name visible to the agent (typically "api"). // services maps service names to their endpoint + header configurations. -// repositoryDir is the local directory for remotely installed configs (empty = no repository). +// repositoryDir is the local directory for remotely installed configs (e.g. api_config; empty = no repository). // workingDir is the base path for resolving relative file paths in resolvers (e.g. resolve_to_base64). // The returned value also implements ServiceProvider for service discovery. func NewApiTool(name string, services map[string]ServiceConfig, repositoryDir, workingDir string) ServiceProvider { @@ -142,7 +142,7 @@ func (a *Api) generateParameters() []core.Parameter { { Name: "configName", Type: "string", - Description: "Service config name to install (filename without .json from the remote repository/api_configs/ folder) — required for install_api_config", + Description: "Service config name to install (filename without .json from the remote repository; installed to api_config/) — required for install_api_config", Required: false, }, {