@aion0/forge 0.5.20 → 0.5.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.forge/agent-context.json +1 -1
- package/RELEASE_NOTES.md +32 -6
- package/app/api/code/route.ts +10 -4
- package/app/api/plugins/route.ts +75 -0
- package/components/Dashboard.tsx +1 -0
- package/components/PipelineEditor.tsx +135 -9
- package/components/PluginsPanel.tsx +472 -0
- package/components/ProjectDetail.tsx +36 -98
- package/components/SessionView.tsx +4 -4
- package/components/SettingsModal.tsx +160 -66
- package/components/SkillsPanel.tsx +14 -5
- package/components/TerminalLauncher.tsx +398 -0
- package/components/WebTerminal.tsx +84 -84
- package/components/WorkspaceView.tsx +371 -87
- package/lib/agents/index.ts +7 -4
- package/lib/builtin-plugins/docker.yaml +70 -0
- package/lib/builtin-plugins/http.yaml +66 -0
- package/lib/builtin-plugins/jenkins.yaml +92 -0
- package/lib/builtin-plugins/llm-vision.yaml +85 -0
- package/lib/builtin-plugins/playwright.yaml +111 -0
- package/lib/builtin-plugins/shell-command.yaml +60 -0
- package/lib/builtin-plugins/slack.yaml +48 -0
- package/lib/builtin-plugins/webhook.yaml +56 -0
- package/lib/forge-mcp-server.ts +116 -2
- package/lib/pipeline.ts +62 -5
- package/lib/plugins/executor.ts +347 -0
- package/lib/plugins/registry.ts +228 -0
- package/lib/plugins/types.ts +103 -0
- package/lib/project-sessions.ts +7 -2
- package/lib/session-utils.ts +7 -3
- package/lib/terminal-standalone.ts +6 -34
- package/lib/workspace/agent-worker.ts +1 -1
- package/lib/workspace/orchestrator.ts +414 -136
- package/lib/workspace/presets.ts +5 -3
- package/lib/workspace/session-monitor.ts +14 -10
- package/lib/workspace/types.ts +3 -1
- package/lib/workspace-standalone.ts +38 -21
- package/next-env.d.ts +1 -1
- package/package.json +1 -1
- package/qa/.forge/agent-context.json +1 -1
package/lib/agents/index.ts
CHANGED
|
@@ -95,6 +95,7 @@ export function listAgents(): AgentConfig[] {
|
|
|
95
95
|
flags,
|
|
96
96
|
enabled: cfg.enabled !== false,
|
|
97
97
|
detected: !!detected,
|
|
98
|
+
cliType: cfg.cliType,
|
|
98
99
|
} as any);
|
|
99
100
|
}
|
|
100
101
|
}
|
|
@@ -218,16 +219,18 @@ export function resolveTerminalLaunch(agentId?: string): TerminalLaunchInfo {
|
|
|
218
219
|
// Resolve env/model: either from this agent's own profile fields, or from linked profile
|
|
219
220
|
let env: Record<string, string> | undefined;
|
|
220
221
|
let model: string | undefined;
|
|
221
|
-
if (agentCfg.base || agentCfg.env || agentCfg.model) {
|
|
222
|
-
// This agent
|
|
222
|
+
if (agentCfg.base || agentCfg.env || agentCfg.model || agentCfg.models) {
|
|
223
|
+
// This agent has profile-like config — read env/model directly
|
|
223
224
|
if (agentCfg.env) env = { ...agentCfg.env };
|
|
224
|
-
|
|
225
|
+
model = agentCfg.model || agentCfg.models?.terminal;
|
|
226
|
+
if (model === 'default') model = undefined; // 'default' means no override
|
|
225
227
|
} else if (agentCfg.profile) {
|
|
226
228
|
// Agent links to a separate profile — read from that
|
|
227
229
|
const profileCfg = settings.agents?.[agentCfg.profile];
|
|
228
230
|
if (profileCfg) {
|
|
229
231
|
if (profileCfg.env) env = { ...profileCfg.env };
|
|
230
|
-
|
|
232
|
+
model = profileCfg.model || profileCfg.models?.terminal;
|
|
233
|
+
if (model === 'default') model = undefined;
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
id: docker
|
|
2
|
+
name: Docker
|
|
3
|
+
icon: "🐳"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Build and manage Docker images.
|
|
8
|
+
Create instances per project or registry:
|
|
9
|
+
- "Build App" → default_image: my-app, default_context: .
|
|
10
|
+
- "Push to Harbor" → registry: harbor.example.com
|
|
11
|
+
|
|
12
|
+
config:
|
|
13
|
+
registry:
|
|
14
|
+
type: string
|
|
15
|
+
label: Registry URL
|
|
16
|
+
description: "e.g., docker.io, ghcr.io, harbor.example.com"
|
|
17
|
+
default_image:
|
|
18
|
+
type: string
|
|
19
|
+
label: Default Image Name
|
|
20
|
+
description: "Pre-configured image name"
|
|
21
|
+
default_tag:
|
|
22
|
+
type: string
|
|
23
|
+
label: Default Tag
|
|
24
|
+
default: "latest"
|
|
25
|
+
default_dockerfile:
|
|
26
|
+
type: string
|
|
27
|
+
label: Default Dockerfile
|
|
28
|
+
default: "Dockerfile"
|
|
29
|
+
default_context:
|
|
30
|
+
type: string
|
|
31
|
+
label: Default Build Context
|
|
32
|
+
default: "."
|
|
33
|
+
|
|
34
|
+
params:
|
|
35
|
+
image:
|
|
36
|
+
type: string
|
|
37
|
+
label: Image Name
|
|
38
|
+
description: "Overrides default_image"
|
|
39
|
+
tag:
|
|
40
|
+
type: string
|
|
41
|
+
label: Tag
|
|
42
|
+
dockerfile:
|
|
43
|
+
type: string
|
|
44
|
+
label: Dockerfile Path
|
|
45
|
+
context:
|
|
46
|
+
type: string
|
|
47
|
+
label: Build Context
|
|
48
|
+
|
|
49
|
+
defaultAction: build
|
|
50
|
+
|
|
51
|
+
actions:
|
|
52
|
+
build:
|
|
53
|
+
run: shell
|
|
54
|
+
command: "docker build -t {{params.image}}:{{params.tag}} -f {{params.dockerfile}} {{params.context}}"
|
|
55
|
+
timeout: 600
|
|
56
|
+
output:
|
|
57
|
+
result: "$stdout"
|
|
58
|
+
|
|
59
|
+
push:
|
|
60
|
+
run: shell
|
|
61
|
+
command: "docker push {{config.registry}}/{{params.image}}:{{params.tag}}"
|
|
62
|
+
timeout: 300
|
|
63
|
+
output:
|
|
64
|
+
result: "$stdout"
|
|
65
|
+
|
|
66
|
+
tag:
|
|
67
|
+
run: shell
|
|
68
|
+
command: "docker tag {{params.image}}:{{params.tag}} {{config.registry}}/{{params.image}}:{{params.tag}}"
|
|
69
|
+
output:
|
|
70
|
+
result: "$stdout"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
id: http
|
|
2
|
+
name: HTTP Request
|
|
3
|
+
icon: "🌐"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Make HTTP requests to any API endpoint.
|
|
8
|
+
Create instances for common API calls:
|
|
9
|
+
- "Health Check" → default_url: https://api.example.com/health, default_method: GET
|
|
10
|
+
- "Deploy Hook" → default_url: https://deploy.example.com/trigger, default_method: POST
|
|
11
|
+
- "Notify Slack" → default_url: https://hooks.slack.com/xxx, default_method: POST
|
|
12
|
+
|
|
13
|
+
config:
|
|
14
|
+
default_url:
|
|
15
|
+
type: string
|
|
16
|
+
label: Default URL
|
|
17
|
+
description: "Pre-configured URL"
|
|
18
|
+
default_method:
|
|
19
|
+
type: select
|
|
20
|
+
label: Default Method
|
|
21
|
+
options: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
|
22
|
+
default: "GET"
|
|
23
|
+
default_headers:
|
|
24
|
+
type: json
|
|
25
|
+
label: Default Headers
|
|
26
|
+
description: 'e.g., {"Authorization": "Bearer xxx"}'
|
|
27
|
+
default_body:
|
|
28
|
+
type: json
|
|
29
|
+
label: Default Body
|
|
30
|
+
description: "Default request body (for POST/PUT)"
|
|
31
|
+
expected_status:
|
|
32
|
+
type: string
|
|
33
|
+
label: Expected Status Code
|
|
34
|
+
description: "e.g., 200, 201 — used to validate response"
|
|
35
|
+
description:
|
|
36
|
+
type: string
|
|
37
|
+
label: Description
|
|
38
|
+
description: "What this request does (shown to agents)"
|
|
39
|
+
|
|
40
|
+
params:
|
|
41
|
+
url:
|
|
42
|
+
type: string
|
|
43
|
+
label: URL
|
|
44
|
+
description: "Overrides default_url if set"
|
|
45
|
+
method:
|
|
46
|
+
type: select
|
|
47
|
+
label: Method
|
|
48
|
+
options: ["GET", "POST", "PUT", "DELETE", "PATCH"]
|
|
49
|
+
headers:
|
|
50
|
+
type: json
|
|
51
|
+
label: Headers
|
|
52
|
+
body:
|
|
53
|
+
type: json
|
|
54
|
+
label: Body
|
|
55
|
+
|
|
56
|
+
defaultAction: request
|
|
57
|
+
|
|
58
|
+
actions:
|
|
59
|
+
request:
|
|
60
|
+
run: http
|
|
61
|
+
method: "{{params.method}}"
|
|
62
|
+
url: "{{params.url}}"
|
|
63
|
+
headers: "{{params.headers}}"
|
|
64
|
+
body: "{{params.body}}"
|
|
65
|
+
output:
|
|
66
|
+
result: "$body"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
id: jenkins
|
|
2
|
+
name: Jenkins CI
|
|
3
|
+
icon: "🔧"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Trigger and monitor Jenkins CI/CD jobs.
|
|
8
|
+
Create instances per job or per Jenkins server:
|
|
9
|
+
- "Build Backend" → default_job: backend-build
|
|
10
|
+
- "E2E Tests" → default_job: e2e-test-suite
|
|
11
|
+
- "Security Scan" → default_job: sonar-scan
|
|
12
|
+
|
|
13
|
+
config:
|
|
14
|
+
url:
|
|
15
|
+
type: string
|
|
16
|
+
label: Jenkins URL
|
|
17
|
+
required: true
|
|
18
|
+
description: "e.g., https://jenkins.example.com"
|
|
19
|
+
user:
|
|
20
|
+
type: string
|
|
21
|
+
label: Username
|
|
22
|
+
required: true
|
|
23
|
+
token:
|
|
24
|
+
type: secret
|
|
25
|
+
label: API Token
|
|
26
|
+
required: true
|
|
27
|
+
default_job:
|
|
28
|
+
type: string
|
|
29
|
+
label: Default Job Name
|
|
30
|
+
description: "Pre-configured job — no need to pass job param each time"
|
|
31
|
+
|
|
32
|
+
params:
|
|
33
|
+
job:
|
|
34
|
+
type: string
|
|
35
|
+
label: Job Name
|
|
36
|
+
description: "Overrides default_job if set"
|
|
37
|
+
parameters:
|
|
38
|
+
type: json
|
|
39
|
+
label: Build Parameters
|
|
40
|
+
description: "JSON object of build parameters"
|
|
41
|
+
build_number:
|
|
42
|
+
type: string
|
|
43
|
+
label: Build Number
|
|
44
|
+
description: "Specific build number (for log/status). Defaults to lastBuild"
|
|
45
|
+
|
|
46
|
+
defaultAction: trigger
|
|
47
|
+
|
|
48
|
+
actions:
|
|
49
|
+
trigger:
|
|
50
|
+
run: http
|
|
51
|
+
method: POST
|
|
52
|
+
url: "{{config.url}}/job/{{params.job}}/buildWithParameters"
|
|
53
|
+
headers:
|
|
54
|
+
Authorization: "Basic {{config.user}}:{{config.token}}"
|
|
55
|
+
body: "{{params.parameters | json}}"
|
|
56
|
+
output:
|
|
57
|
+
queue_url: "$.headers.location"
|
|
58
|
+
|
|
59
|
+
wait:
|
|
60
|
+
run: poll
|
|
61
|
+
url: "{{config.url}}/job/{{params.job}}/lastBuild/api/json"
|
|
62
|
+
headers:
|
|
63
|
+
Authorization: "Basic {{config.user}}:{{config.token}}"
|
|
64
|
+
interval: 30
|
|
65
|
+
until: "$.result != null"
|
|
66
|
+
timeout: 3600
|
|
67
|
+
output:
|
|
68
|
+
result: "$.result"
|
|
69
|
+
duration: "$.duration"
|
|
70
|
+
url: "$.url"
|
|
71
|
+
build_number: "$.number"
|
|
72
|
+
|
|
73
|
+
get_log:
|
|
74
|
+
run: http
|
|
75
|
+
method: GET
|
|
76
|
+
url: "{{config.url}}/job/{{params.job}}/{{params.build_number}}/consoleText"
|
|
77
|
+
headers:
|
|
78
|
+
Authorization: "Basic {{config.user}}:{{config.token}}"
|
|
79
|
+
output:
|
|
80
|
+
log: "$body"
|
|
81
|
+
|
|
82
|
+
status:
|
|
83
|
+
run: http
|
|
84
|
+
method: GET
|
|
85
|
+
url: "{{config.url}}/job/{{params.job}}/lastBuild/api/json"
|
|
86
|
+
headers:
|
|
87
|
+
Authorization: "Basic {{config.user}}:{{config.token}}"
|
|
88
|
+
output:
|
|
89
|
+
result: "$.result"
|
|
90
|
+
building: "$.building"
|
|
91
|
+
number: "$.number"
|
|
92
|
+
timestamp: "$.timestamp"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
id: llm-vision
|
|
2
|
+
name: LLM Vision
|
|
3
|
+
icon: "👁"
|
|
4
|
+
version: "0.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Send images to vision-capable LLMs for evaluation.
|
|
8
|
+
Supports OpenAI (GPT-4o), Anthropic (Claude), Google (Gemini).
|
|
9
|
+
Create instances per model for multi-model evaluation.
|
|
10
|
+
|
|
11
|
+
config:
|
|
12
|
+
provider:
|
|
13
|
+
type: select
|
|
14
|
+
label: Provider
|
|
15
|
+
options: ["openai", "anthropic", "google"]
|
|
16
|
+
required: true
|
|
17
|
+
api_key:
|
|
18
|
+
type: secret
|
|
19
|
+
label: API Key
|
|
20
|
+
required: true
|
|
21
|
+
model:
|
|
22
|
+
type: string
|
|
23
|
+
label: Model
|
|
24
|
+
description: "e.g., gpt-4o, claude-sonnet-4-6, gemini-2.0-flash"
|
|
25
|
+
default: "gpt-4o"
|
|
26
|
+
default_prompt:
|
|
27
|
+
type: string
|
|
28
|
+
label: Default Evaluation Prompt
|
|
29
|
+
description: "Default prompt if none provided per-call"
|
|
30
|
+
|
|
31
|
+
params:
|
|
32
|
+
image:
|
|
33
|
+
type: string
|
|
34
|
+
label: Image file path
|
|
35
|
+
required: true
|
|
36
|
+
description: "Local path to image file"
|
|
37
|
+
prompt:
|
|
38
|
+
type: string
|
|
39
|
+
label: Evaluation prompt
|
|
40
|
+
description: "What to evaluate — overrides default_prompt"
|
|
41
|
+
max_tokens:
|
|
42
|
+
type: number
|
|
43
|
+
label: Max tokens
|
|
44
|
+
default: 1024
|
|
45
|
+
|
|
46
|
+
defaultAction: evaluate
|
|
47
|
+
|
|
48
|
+
actions:
|
|
49
|
+
evaluate:
|
|
50
|
+
run: shell
|
|
51
|
+
command: |
|
|
52
|
+
PROVIDER="{{config.provider}}"
|
|
53
|
+
IMAGE="{{params.image}}"
|
|
54
|
+
PROMPT="{{params.prompt}}"
|
|
55
|
+
MODEL="{{config.model}}"
|
|
56
|
+
MAX_TOKENS="{{params.max_tokens}}"
|
|
57
|
+
[ -z "$MAX_TOKENS" ] && MAX_TOKENS=1024
|
|
58
|
+
IMG_BASE64=$(base64 < "$IMAGE" | tr -d '\n')
|
|
59
|
+
MIME="image/png"
|
|
60
|
+
case "$IMAGE" in *.jpg|*.jpeg) MIME="image/jpeg";; *.webp) MIME="image/webp";; esac
|
|
61
|
+
|
|
62
|
+
if [ "$PROVIDER" = "openai" ]; then
|
|
63
|
+
curl -s https://api.openai.com/v1/chat/completions \
|
|
64
|
+
-H "Authorization: Bearer {{config.api_key}}" \
|
|
65
|
+
-H "Content-Type: application/json" \
|
|
66
|
+
-d "{\"model\":\"$MODEL\",\"max_tokens\":$MAX_TOKENS,\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"$PROMPT\"},{\"type\":\"image_url\",\"image_url\":{\"url\":\"data:$MIME;base64,$IMG_BASE64\"}}]}]}" \
|
|
67
|
+
| python3 -c "import sys,json; print(json.load(sys.stdin)['choices'][0]['message']['content'])" 2>&1
|
|
68
|
+
|
|
69
|
+
elif [ "$PROVIDER" = "anthropic" ]; then
|
|
70
|
+
curl -s https://api.anthropic.com/v1/messages \
|
|
71
|
+
-H "x-api-key: {{config.api_key}}" \
|
|
72
|
+
-H "anthropic-version: 2023-06-01" \
|
|
73
|
+
-H "Content-Type: application/json" \
|
|
74
|
+
-d "{\"model\":\"$MODEL\",\"max_tokens\":$MAX_TOKENS,\"messages\":[{\"role\":\"user\",\"content\":[{\"type\":\"text\",\"text\":\"$PROMPT\"},{\"type\":\"image\",\"source\":{\"type\":\"base64\",\"media_type\":\"$MIME\",\"data\":\"$IMG_BASE64\"}}]}]}" \
|
|
75
|
+
| python3 -c "import sys,json; r=json.load(sys.stdin); print(r['content'][0]['text'])" 2>&1
|
|
76
|
+
|
|
77
|
+
elif [ "$PROVIDER" = "google" ]; then
|
|
78
|
+
curl -s "https://generativelanguage.googleapis.com/v1beta/models/$MODEL:generateContent?key={{config.api_key}}" \
|
|
79
|
+
-H "Content-Type: application/json" \
|
|
80
|
+
-d "{\"contents\":[{\"parts\":[{\"text\":\"$PROMPT\"},{\"inline_data\":{\"mime_type\":\"$MIME\",\"data\":\"$IMG_BASE64\"}}]}]}" \
|
|
81
|
+
| python3 -c "import sys,json; r=json.load(sys.stdin); print(r['candidates'][0]['content']['parts'][0]['text'])" 2>&1
|
|
82
|
+
fi
|
|
83
|
+
timeout: 120
|
|
84
|
+
output:
|
|
85
|
+
result: "$stdout"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
id: playwright
|
|
2
|
+
name: Playwright
|
|
3
|
+
icon: "🎭"
|
|
4
|
+
version: "0.3.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Browser testing and screenshots via Playwright.
|
|
8
|
+
Create one instance per environment (local, docker, or remote).
|
|
9
|
+
Forge does not install Playwright. Set up your own environment:
|
|
10
|
+
- Local: https://playwright.dev/docs/intro
|
|
11
|
+
- Docker: docker pull mcr.microsoft.com/playwright:v1.52.0-noble
|
|
12
|
+
- Remote: https://www.browserless.io/docs
|
|
13
|
+
|
|
14
|
+
config:
|
|
15
|
+
mode:
|
|
16
|
+
type: select
|
|
17
|
+
label: Execution Mode
|
|
18
|
+
options: ["local", "docker", "remote"]
|
|
19
|
+
default: "local"
|
|
20
|
+
required: true
|
|
21
|
+
base_url:
|
|
22
|
+
type: string
|
|
23
|
+
label: Application URL
|
|
24
|
+
description: "URL of the app being tested"
|
|
25
|
+
default: "http://localhost:3000"
|
|
26
|
+
required: true
|
|
27
|
+
server_url:
|
|
28
|
+
type: string
|
|
29
|
+
label: Remote WebSocket URL
|
|
30
|
+
description: "For remote mode (e.g., ws://playwright:3000)"
|
|
31
|
+
docker_image:
|
|
32
|
+
type: string
|
|
33
|
+
label: Docker Image
|
|
34
|
+
default: "mcr.microsoft.com/playwright:v1.52.0-noble"
|
|
35
|
+
project_dir:
|
|
36
|
+
type: string
|
|
37
|
+
label: Project Directory
|
|
38
|
+
description: "For docker mode — local path to mount as /app"
|
|
39
|
+
headless:
|
|
40
|
+
type: select
|
|
41
|
+
label: Headless Mode
|
|
42
|
+
options: ["true", "false"]
|
|
43
|
+
default: "true"
|
|
44
|
+
description: "false = show browser window (local mode only)"
|
|
45
|
+
|
|
46
|
+
params:
|
|
47
|
+
test_file:
|
|
48
|
+
type: string
|
|
49
|
+
label: Test file or directory
|
|
50
|
+
url:
|
|
51
|
+
type: string
|
|
52
|
+
label: URL to screenshot
|
|
53
|
+
args:
|
|
54
|
+
type: string
|
|
55
|
+
label: Extra CLI arguments
|
|
56
|
+
|
|
57
|
+
defaultAction: test
|
|
58
|
+
|
|
59
|
+
actions:
|
|
60
|
+
# Local mode
|
|
61
|
+
local_test:
|
|
62
|
+
run: shell
|
|
63
|
+
command: "BASE_URL={{config.base_url}} npx playwright test {{params.test_file}} {{params.args}} --reporter=line $([ '{{config.headless}}' = 'false' ] && echo '--headed') 2>&1"
|
|
64
|
+
timeout: 600
|
|
65
|
+
output:
|
|
66
|
+
result: "$stdout"
|
|
67
|
+
|
|
68
|
+
local_screenshot:
|
|
69
|
+
run: shell
|
|
70
|
+
command: "npx playwright screenshot --wait-for-timeout=3000 {{config.base_url}} /tmp/forge-screenshot-$(date +%s).png 2>&1"
|
|
71
|
+
timeout: 60
|
|
72
|
+
output:
|
|
73
|
+
result: "$stdout"
|
|
74
|
+
|
|
75
|
+
# Docker mode
|
|
76
|
+
docker_test:
|
|
77
|
+
run: shell
|
|
78
|
+
command: "docker run --rm -e BASE_URL={{config.base_url}} -v {{config.project_dir}}:/app -w /app {{config.docker_image}} npx playwright test {{params.test_file}} {{params.args}} --reporter=line 2>&1"
|
|
79
|
+
timeout: 600
|
|
80
|
+
output:
|
|
81
|
+
result: "$stdout"
|
|
82
|
+
|
|
83
|
+
docker_screenshot:
|
|
84
|
+
run: shell
|
|
85
|
+
command: "docker run --rm {{config.docker_image}} npx playwright screenshot --wait-for-timeout=3000 {{config.base_url}} /tmp/screenshot.png 2>&1"
|
|
86
|
+
timeout: 60
|
|
87
|
+
output:
|
|
88
|
+
result: "$stdout"
|
|
89
|
+
|
|
90
|
+
# Remote mode
|
|
91
|
+
remote_test:
|
|
92
|
+
run: shell
|
|
93
|
+
command: "PW_TEST_CONNECT_WS_ENDPOINT={{config.server_url}} BASE_URL={{config.base_url}} npx playwright test {{params.test_file}} {{params.args}} --reporter=line 2>&1"
|
|
94
|
+
timeout: 600
|
|
95
|
+
output:
|
|
96
|
+
result: "$stdout"
|
|
97
|
+
|
|
98
|
+
remote_screenshot:
|
|
99
|
+
run: shell
|
|
100
|
+
command: "PW_TEST_CONNECT_WS_ENDPOINT={{config.server_url}} npx playwright screenshot --wait-for-timeout=3000 {{config.base_url}} /tmp/forge-screenshot-$(date +%s).png 2>&1"
|
|
101
|
+
timeout: 60
|
|
102
|
+
output:
|
|
103
|
+
result: "$stdout"
|
|
104
|
+
|
|
105
|
+
# Common (all modes)
|
|
106
|
+
check_url:
|
|
107
|
+
run: shell
|
|
108
|
+
command: "curl -sf -o /dev/null -w '%{http_code}' {{config.base_url}}"
|
|
109
|
+
timeout: 10
|
|
110
|
+
output:
|
|
111
|
+
status_code: "$stdout"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
id: shell-command
|
|
2
|
+
name: Shell Command
|
|
3
|
+
icon: "💻"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Run shell commands with structured output.
|
|
8
|
+
Create instances for common operations:
|
|
9
|
+
- "Run Tests" → default_command: npm test
|
|
10
|
+
- "Build" → default_command: npm run build
|
|
11
|
+
- "Deploy Staging" → default_command: ssh staging 'cd /app && git pull && pm2 restart'
|
|
12
|
+
|
|
13
|
+
config:
|
|
14
|
+
default_command:
|
|
15
|
+
type: string
|
|
16
|
+
label: Default Command
|
|
17
|
+
description: "Pre-configured command to run if none provided"
|
|
18
|
+
default_cwd:
|
|
19
|
+
type: string
|
|
20
|
+
label: Working Directory
|
|
21
|
+
description: "Default working directory"
|
|
22
|
+
default_timeout:
|
|
23
|
+
type: number
|
|
24
|
+
label: Timeout (seconds)
|
|
25
|
+
default: 300
|
|
26
|
+
expected_output:
|
|
27
|
+
type: string
|
|
28
|
+
label: Expected Output Pattern
|
|
29
|
+
description: "Regex pattern to validate output (e.g., 'SUCCESS|PASSED')"
|
|
30
|
+
description:
|
|
31
|
+
type: string
|
|
32
|
+
label: Description
|
|
33
|
+
description: "What this command does (shown to agents)"
|
|
34
|
+
|
|
35
|
+
params:
|
|
36
|
+
command:
|
|
37
|
+
type: string
|
|
38
|
+
label: Command
|
|
39
|
+
description: "Overrides default_command if set"
|
|
40
|
+
cwd:
|
|
41
|
+
type: string
|
|
42
|
+
label: Working Directory
|
|
43
|
+
timeout:
|
|
44
|
+
type: number
|
|
45
|
+
label: Timeout (seconds)
|
|
46
|
+
args:
|
|
47
|
+
type: string
|
|
48
|
+
label: Extra Arguments
|
|
49
|
+
description: "Appended to the command"
|
|
50
|
+
|
|
51
|
+
defaultAction: run
|
|
52
|
+
|
|
53
|
+
actions:
|
|
54
|
+
run:
|
|
55
|
+
run: shell
|
|
56
|
+
command: "{{params.command}} {{params.args}}"
|
|
57
|
+
cwd: "{{params.cwd}}"
|
|
58
|
+
timeout: 300
|
|
59
|
+
output:
|
|
60
|
+
stdout: "$stdout"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
id: slack
|
|
2
|
+
name: Slack
|
|
3
|
+
icon: "📢"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Send messages to Slack channels via Incoming Webhook.
|
|
8
|
+
Create instances per channel or workspace:
|
|
9
|
+
- "Dev Alerts" → webhook for #dev-alerts
|
|
10
|
+
- "Deploy Notify" → webhook for #deployments
|
|
11
|
+
|
|
12
|
+
config:
|
|
13
|
+
webhook_url:
|
|
14
|
+
type: secret
|
|
15
|
+
label: Webhook URL
|
|
16
|
+
required: true
|
|
17
|
+
description: "Slack Incoming Webhook URL"
|
|
18
|
+
default_channel:
|
|
19
|
+
type: string
|
|
20
|
+
label: Default Channel
|
|
21
|
+
description: "Override channel (e.g., #dev-alerts)"
|
|
22
|
+
description:
|
|
23
|
+
type: string
|
|
24
|
+
label: Description
|
|
25
|
+
description: "What this webhook is for (shown to agents)"
|
|
26
|
+
|
|
27
|
+
params:
|
|
28
|
+
text:
|
|
29
|
+
type: string
|
|
30
|
+
label: Message
|
|
31
|
+
required: true
|
|
32
|
+
channel:
|
|
33
|
+
type: string
|
|
34
|
+
label: Channel
|
|
35
|
+
description: "Overrides default_channel"
|
|
36
|
+
|
|
37
|
+
defaultAction: send
|
|
38
|
+
|
|
39
|
+
actions:
|
|
40
|
+
send:
|
|
41
|
+
run: http
|
|
42
|
+
method: POST
|
|
43
|
+
url: "{{config.webhook_url}}"
|
|
44
|
+
headers:
|
|
45
|
+
Content-Type: application/json
|
|
46
|
+
body: '{"text":"{{params.text}}","channel":"{{params.channel}}"}'
|
|
47
|
+
output:
|
|
48
|
+
status: "$body"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
id: webhook
|
|
2
|
+
name: Webhook
|
|
3
|
+
icon: "🪝"
|
|
4
|
+
version: "1.1.0"
|
|
5
|
+
author: forge
|
|
6
|
+
description: |
|
|
7
|
+
Send webhook notifications to any URL.
|
|
8
|
+
Create instances per endpoint:
|
|
9
|
+
- "Deploy Hook" → url: https://deploy.example.com/hook
|
|
10
|
+
- "CI Trigger" → url: https://ci.example.com/trigger
|
|
11
|
+
|
|
12
|
+
config:
|
|
13
|
+
url:
|
|
14
|
+
type: string
|
|
15
|
+
label: Webhook URL
|
|
16
|
+
required: true
|
|
17
|
+
description: "Target URL to send webhooks to"
|
|
18
|
+
secret:
|
|
19
|
+
type: secret
|
|
20
|
+
label: Secret
|
|
21
|
+
description: "Shared secret for signature verification (optional)"
|
|
22
|
+
default_event:
|
|
23
|
+
type: string
|
|
24
|
+
label: Default Event Type
|
|
25
|
+
default: "forge.notification"
|
|
26
|
+
description: "Sent as X-Forge-Event header"
|
|
27
|
+
default_method:
|
|
28
|
+
type: select
|
|
29
|
+
label: HTTP Method
|
|
30
|
+
options: ["POST", "PUT", "PATCH"]
|
|
31
|
+
default: "POST"
|
|
32
|
+
|
|
33
|
+
params:
|
|
34
|
+
payload:
|
|
35
|
+
type: json
|
|
36
|
+
label: Payload
|
|
37
|
+
description: "JSON body to send"
|
|
38
|
+
event:
|
|
39
|
+
type: string
|
|
40
|
+
label: Event Type
|
|
41
|
+
description: "Overrides default_event"
|
|
42
|
+
|
|
43
|
+
defaultAction: send
|
|
44
|
+
|
|
45
|
+
actions:
|
|
46
|
+
send:
|
|
47
|
+
run: http
|
|
48
|
+
method: "{{params.method}}"
|
|
49
|
+
url: "{{config.url}}"
|
|
50
|
+
headers:
|
|
51
|
+
Content-Type: application/json
|
|
52
|
+
X-Forge-Event: "{{params.event}}"
|
|
53
|
+
X-Forge-Secret: "{{config.secret}}"
|
|
54
|
+
body: "{{params | json}}"
|
|
55
|
+
output:
|
|
56
|
+
status: "$body"
|