Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions .github/aw/actions-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,31 +165,6 @@
"version": "v3",
"sha": "3477b6488008d9411aaf22a0924ec7c1f6a69980"
},
"github/stale-repos@v3.0.2": {
"repo": "github/stale-repos",
"version": "v3.0.2",
"sha": "a21e55567b83cf3c3f3f9085d3038dc6cee02598"
},
"haskell-actions/setup@v2.10.3": {
"repo": "haskell-actions/setup",
"version": "v2.10.3",
"sha": "9cd1b7bf3f36d5a3c3b17abc3545bfb5481912ea"
},
"oven-sh/setup-bun@v2.1.2": {
"repo": "oven-sh/setup-bun",
"version": "v2.1.2",
"sha": "3d267786b128fe76c2f16a390aa2448b815359f3"
},
"ruby/setup-ruby@v1.288.0": {
"repo": "ruby/setup-ruby",
"version": "v1.288.0",
"sha": "09a7688d3b55cf0e976497ff046b70949eeaccfd"
},
"super-linter/super-linter@v8.2.1": {
"repo": "super-linter/super-linter",
"version": "v8.2.1",
"sha": "2bdd90ed3262e023ac84bf8fe35dc480721fc1f2"
},
"super-linter/super-linter@v8.5.0": {
"repo": "super-linter/super-linter",
"version": "v8.5.0",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs-noob-tester.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/slide-deck-maintainer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .github/workflows/stale-repo-identifier.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions docs/src/content/docs/reference/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ A GitHub Personal Access Token with granular permission control, specifying exac

The `gh-aw` extension for GitHub CLI providing commands for managing agentic workflows: compile, run, status, logs, add, and project management.

### Playground

An interactive web-based editor for authoring, compiling, and previewing agentic workflows without local installation. The Playground runs the gh-aw compiler in the browser using [WebAssembly](#webassembly-wasm) and auto-saves editor content to `localStorage` so work is preserved across sessions. Available at `/gh-aw/editor/`.

### Validation

Checking workflow files for errors, security issues, and best practices. Occurs during compilation and can be enhanced with strict mode and security scanners.
Expand Down
25 changes: 0 additions & 25 deletions pkg/workflow/data/action_pins.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,31 +165,6 @@
"version": "v3",
"sha": "3477b6488008d9411aaf22a0924ec7c1f6a69980"
},
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

action_pins.json no longer includes a pin for github/stale-repos@v3.0.2, but .github/workflows/stale-repo-identifier.md uses uses: github/stale-repos@v3.0.2 with strict: true. In strict mode, GetActionPinWithData requires an exact version match in action_pins.json when dynamic resolution isn’t available, so this can leave the action unpinned (or emit warnings) and break deterministic lock generation. Re-add the v3.0.2 entry (or update the workflow source to use @V3 and regenerate locks).

Suggested change
},
},
"github/stale-repos@v3.0.2": {
"repo": "github/stale-repos",
"version": "v3.0.2",
"sha": "3477b6488008d9411aaf22a0924ec7c1f6a69980"
},

Copilot uses AI. Check for mistakes.
"github/stale-repos@v3.0.2": {
"repo": "github/stale-repos",
"version": "v3.0.2",
"sha": "a21e55567b83cf3c3f3f9085d3038dc6cee02598"
},
"haskell-actions/setup@v2.10.3": {
"repo": "haskell-actions/setup",
"version": "v2.10.3",
"sha": "9cd1b7bf3f36d5a3c3b17abc3545bfb5481912ea"
},
"oven-sh/setup-bun@v2.1.2": {
"repo": "oven-sh/setup-bun",
"version": "v2.1.2",
"sha": "3d267786b128fe76c2f16a390aa2448b815359f3"
},
"ruby/setup-ruby@v1.288.0": {
"repo": "ruby/setup-ruby",
"version": "v1.288.0",
"sha": "09a7688d3b55cf0e976497ff046b70949eeaccfd"
},
"super-linter/super-linter@v8.2.1": {
"repo": "super-linter/super-linter",
"version": "v8.2.1",
"sha": "2bdd90ed3262e023ac84bf8fe35dc480721fc1f2"
},
"super-linter/super-linter@v8.5.0": {
"repo": "super-linter/super-linter",
"version": "v8.5.0",
Expand Down
33 changes: 33 additions & 0 deletions pkg/workflow/domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ var ClaudeDefaultDomains = []string{
"ts-ocsp.ws.symantec.com",
}

// PlaywrightDomains are the domains required for Playwright browser downloads
// These domains are needed when Playwright MCP server initializes in the Docker container
var PlaywrightDomains = []string{
"cdn.playwright.dev",
"playwright.download.prss.microsoft.com",
}

// init loads the ecosystem domains from the embedded JSON
func init() {
domainsLog.Print("Loading ecosystem domains from embedded JSON")
Expand Down Expand Up @@ -349,6 +356,23 @@ func extractHTTPMCPDomains(tools map[string]any) []string {
return domains
}

// extractPlaywrightDomains returns Playwright domains when Playwright tool is configured
// Returns a slice of domain names required for Playwright browser downloads
// These domains are needed when Playwright MCP server initializes in the Docker container
func extractPlaywrightDomains(tools map[string]any) []string {
if tools == nil {
return []string{}
}

// Check if Playwright tool is configured
if _, hasPlaywright := tools["playwright"]; hasPlaywright {
domainsLog.Printf("Detected Playwright tool, adding %d domains for browser downloads", len(PlaywrightDomains))
return PlaywrightDomains
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extractPlaywrightDomains returns the package-level PlaywrightDomains slice directly. Because slices are mutable, any caller that sorts/appends to the returned slice can unintentionally mutate the global allow-list (and this also diverges from getEcosystemDomains(), which explicitly returns a copied slice). Prefer returning a defensive copy (or better, source this from getEcosystemDomains("playwright") since the same domains already live in ecosystem_domains.json) to keep a single source of truth and avoid shared-mutation bugs.

Suggested change
return PlaywrightDomains
// Return a defensive copy so callers cannot mutate the global PlaywrightDomains slice
copiedDomains := make([]string, len(PlaywrightDomains))
copy(copiedDomains, PlaywrightDomains)
return copiedDomains

Copilot uses AI. Check for mistakes.
}

return []string{}
}

// mergeDomainsWithNetwork combines default domains with NetworkPermissions allowed domains
// Returns a deduplicated, sorted, comma-separated string suitable for AWF's --allow-domains flag
func mergeDomainsWithNetwork(defaultDomains []string, network *NetworkPermissions) string {
Expand Down Expand Up @@ -388,6 +412,15 @@ func mergeDomainsWithNetworkToolsAndRuntimes(defaultDomains []string, network *N
}
}

// Add Playwright ecosystem domains (if Playwright tool is specified)
// This ensures browser binaries can be downloaded when Playwright initializes
if tools != nil {
playwrightDomains := extractPlaywrightDomains(tools)
for _, domain := range playwrightDomains {
domainMap[domain] = true
}
}

// Add runtime ecosystem domains (if runtimes are specified)
if runtimes != nil {
runtimeDomains := getDomainsFromRuntimes(runtimes)
Expand Down
104 changes: 104 additions & 0 deletions pkg/workflow/http_mcp_domains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,107 @@ func TestGetClaudeAllowedDomainsWithTools(t *testing.T) {
require.Contains(t, result, "anthropic.com", "Should include Claude defaults")
require.Contains(t, result, "registry.npmjs.org", "Should include Node ecosystem")
}

// TestExtractPlaywrightDomains tests extraction of Playwright ecosystem domains when Playwright tool is configured
func TestExtractPlaywrightDomains(t *testing.T) {
tests := []struct {
name string
tools map[string]any
expected []string
}{
{
name: "playwright tool configured",
tools: map[string]any{
"playwright": map[string]any{
"allowed_domains": []string{"github.com"},
},
},
expected: []string{"playwright.download.prss.microsoft.com", "cdn.playwright.dev"},
},
{
name: "playwright tool with empty config",
tools: map[string]any{
"playwright": map[string]any{},
},
expected: []string{"playwright.download.prss.microsoft.com", "cdn.playwright.dev"},
},
{
name: "playwright tool with null config",
tools: map[string]any{
"playwright": nil,
},
expected: []string{"playwright.download.prss.microsoft.com", "cdn.playwright.dev"},
},
{
name: "no playwright tool",
tools: map[string]any{
"github": map[string]any{
"mode": "local",
},
},
expected: []string{},
},
{
name: "empty tools",
tools: map[string]any{},
expected: []string{},
},
{
name: "nil tools",
tools: nil,
expected: []string{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := extractPlaywrightDomains(tt.tools)

// Sort both slices for comparison
SortStrings(result)
SortStrings(tt.expected)

assert.Equal(t, tt.expected, result, "Extracted Playwright domains should match expected")
})
}
}

// TestGetCopilotAllowedDomainsWithPlaywright tests that Playwright domains are automatically included for Copilot engine
func TestGetCopilotAllowedDomainsWithPlaywright(t *testing.T) {
network := &NetworkPermissions{
Allowed: []string{"defaults"},
}

tools := map[string]any{
"playwright": map[string]any{
"allowed_domains": []string{"github.com"},
},
}

result := GetCopilotAllowedDomainsWithTools(network, tools)

// Should include Copilot defaults and Playwright ecosystem domains
require.Contains(t, result, "playwright.download.prss.microsoft.com", "Should include Playwright download domain")
require.Contains(t, result, "cdn.playwright.dev", "Should include Playwright CDN domain")
require.Contains(t, result, "api.githubcopilot.com", "Should include Copilot defaults")
}

// TestGetCodexAllowedDomainsWithPlaywright tests that Playwright domains are automatically included for Codex engine
func TestGetCodexAllowedDomainsWithPlaywright(t *testing.T) {
network := &NetworkPermissions{
Allowed: []string{"defaults"},
}

tools := map[string]any{
"playwright": map[string]any{
"allowed_domains": []string{"example.com"},
},
}

result := GetCodexAllowedDomainsWithTools(network, tools)

// Should include Codex defaults and Playwright ecosystem domains
require.Contains(t, result, "playwright.download.prss.microsoft.com", "Should include Playwright download domain")
require.Contains(t, result, "cdn.playwright.dev", "Should include Playwright CDN domain")
require.Contains(t, result, "api.openai.com", "Should include Codex defaults")
}