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
38 changes: 38 additions & 0 deletions examples/marketplace/agent-code-reviewer/anyclaw.artifact.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"artifact": {
"id": "cloud.agent.example-code-reviewer",
"kind": "agent",
"name": "Example Code Reviewer Agent",
"summary": "Reviews local changes and highlights concrete risks before merge.",
"description_md": "A minimal AnyClaw marketplace agent example used for publishing smoke tests.",
"latest_version": "1.0.0",
"source": "anyclaw-cloud",
"risk_level": "medium",
"trust_level": "verified",
"permissions": ["fs.read", "git.read"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
},
"tags": ["agent", "review", "quality"],
"hit_signals": ["code review", "pull request", "风险检查"],
"manifest_summary": {
"entry": "agent/profile.json"
}
},
"versions": [
{
"version": "1.0.0",
"released_at": "2026-05-07T00:00:00Z",
"changelog_md": "Initial example marketplace agent.",
"permissions": ["fs.read", "git.read"],
"permissions_diff": ["fs.read", "git.read"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"artifact": {
"id": "anyclaw.agent.marketplace-operator",
"kind": "agent",
"name": "Marketplace Operator",
"summary": "Plans marketplace releases, checks publish readiness, and prepares audit-friendly rollout notes.",
"description_md": "Marketplace Operator is an AnyClaw agent for running a small cloud marketplace safely. It helps maintain artifact metadata, review release notes, verify risk and permission declarations, and prepare publish or quarantine actions for an administrator to approve.",
"latest_version": "1.0.0",
"source": "anyclaw-cloud",
"publisher": "AnyClaw Labs",
"risk_level": "medium",
"trust_level": "verified",
"permissions": ["fs.read", "git.read", "network.registry"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
},
"tags": ["agent", "marketplace", "release", "audit"],
"hit_signals": ["publish artifact", "marketplace release", "release review", "quarantine", "audit"],
"score": 0.98,
"manifest_summary": {
"entry": "agent/profile.json",
"use_case": "marketplace operations"
}
},
"versions": [
{
"version": "1.0.0",
"released_at": "2026-05-07T00:00:00Z",
"changelog_md": "Initial Marketplace Operator agent for release planning, metadata review, and audit-oriented marketplace operations.",
"permissions": ["fs.read", "git.read", "network.registry"],
"permissions_diff": ["fs.read", "git.read", "network.registry"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
}
}
]
}
41 changes: 41 additions & 0 deletions examples/marketplace/cli-agent-native-runner/anyclaw.artifact.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"artifact": {
"id": "anyclaw.cli.agent-native-runner",
"kind": "cli",
"name": "Agent Native Runner",
"summary": "Wraps project commands as agent-friendly CLI actions with predictable inputs, outputs, and safety checks.",
"description_md": "Agent Native Runner is an AnyClaw CLI artifact for exposing repeatable project commands to agents in a predictable way. It follows the same product direction as agent-native CLI hubs: make command-line tools discoverable, installable, and safe for agent workflows without hiding execution risk.",
"latest_version": "1.0.0",
"source": "anyclaw-cloud",
"publisher": "AnyClaw Labs",
"risk_level": "medium",
"trust_level": "verified",
"permissions": ["process.exec", "fs.read", "fs.write"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
},
"tags": ["cli", "agent-native", "automation", "commands", "workflow"],
"hit_signals": ["agent-native cli", "wrap command", "CLI hub", "automation command", "run tool"],
"score": 0.96,
"manifest_summary": {
"command": "anyclaw-agent-runner",
"use_case": "agent-native command wrapper"
}
},
"versions": [
{
"version": "1.0.0",
"released_at": "2026-05-07T00:00:00Z",
"changelog_md": "Initial Agent Native Runner CLI artifact for agent-friendly command wrappers.",
"permissions": ["process.exec", "fs.read", "fs.write"],
"permissions_diff": ["process.exec", "fs.read", "fs.write"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
}
}
]
}
38 changes: 38 additions & 0 deletions examples/marketplace/cli-repo-health/anyclaw.artifact.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"artifact": {
"id": "cloud.cli.example-repo-health",
"kind": "cli",
"name": "Example Repo Health CLI",
"summary": "Runs a lightweight repository health check command.",
"description_md": "A minimal AnyClaw marketplace CLI example used for publishing smoke tests.",
"latest_version": "1.0.0",
"source": "anyclaw-cloud",
"risk_level": "medium",
"trust_level": "verified",
"permissions": ["process.exec", "fs.read"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
},
"tags": ["cli", "health", "repository"],
"hit_signals": ["repo health", "诊断", "cli"],
"manifest_summary": {
"command": "anyclaw-repo-health"
}
},
"versions": [
{
"version": "1.0.0",
"released_at": "2026-05-07T00:00:00Z",
"changelog_md": "Initial example marketplace CLI.",
"permissions": ["process.exec", "fs.read"],
"permissions_diff": ["process.exec", "fs.read"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
}
}
]
}
38 changes: 38 additions & 0 deletions examples/marketplace/skill-release-notes/anyclaw.artifact.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"artifact": {
"id": "cloud.skill.example-release-notes",
"kind": "skill",
"name": "Example Release Notes Skill",
"summary": "Turns git history and release notes into a compact changelog draft.",
"description_md": "A minimal AnyClaw marketplace skill example used to verify publisher-token based publishing.",
"latest_version": "1.0.0",
"source": "anyclaw-cloud",
"risk_level": "low",
"trust_level": "verified",
"permissions": ["fs.read", "git.read"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
},
"tags": ["skill", "release", "writing"],
"hit_signals": ["release notes", "changelog", "发布说明"],
"manifest_summary": {
"entry": "skill/SKILL.md"
}
},
"versions": [
{
"version": "1.0.0",
"released_at": "2026-05-07T00:00:00Z",
"changelog_md": "Initial example marketplace skill.",
"permissions": ["fs.read", "git.read"],
"permissions_diff": ["fs.read", "git.read"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
}
}
]
}
41 changes: 41 additions & 0 deletions examples/marketplace/skill-skill-author/anyclaw.artifact.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"artifact": {
"id": "anyclaw.skill.skill-author",
"kind": "skill",
"name": "Skill Author",
"summary": "Drafts high-quality AnyClaw skills with clear triggers, workflows, safety notes, and validation steps.",
"description_md": "Skill Author helps turn repeatable work into an installable AnyClaw skill. It focuses on SKILL.md structure, trigger clarity, minimal dependency footprint, examples, validation steps, and marketplace-ready metadata. It is inspired by skill-market patterns such as public skill catalogs, but uses AnyClaw's own artifact contract.",
"latest_version": "1.0.0",
"source": "anyclaw-cloud",
"publisher": "AnyClaw Labs",
"risk_level": "low",
"trust_level": "verified",
"permissions": ["fs.read", "fs.write"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
},
"tags": ["skill", "authoring", "SKILL.md", "marketplace", "documentation"],
"hit_signals": ["create skill", "SKILL.md", "skill authoring", "marketplace skill", "authoring"],
"score": 0.97,
"manifest_summary": {
"entry": "skill/SKILL.md",
"use_case": "skill authoring"
}
},
"versions": [
{
"version": "1.0.0",
"released_at": "2026-05-07T00:00:00Z",
"changelog_md": "Initial Skill Author skill for creating AnyClaw marketplace-ready skills.",
"permissions": ["fs.read", "fs.write"],
"permissions_diff": ["fs.read", "fs.write"],
"compatibility": {
"anyclaw_min": "0.1.0",
"os": ["windows", "linux", "darwin"],
"arch": ["amd64", "arm64"]
}
}
]
}
53 changes: 53 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ func clearConfigEnv(t *testing.T) {
"ANYCLAW_WEBHOOK_SECRET",
"ANYCLAW_RATE_LIMIT_RPM",
"ANYCLAW_PLUGIN_EXEC_TIMEOUT",
"ANYCLAW_MARKETPLACE_ENDPOINT",
"ANYCLAW_REGISTRY_TOKEN",
"ANYCLAW_MARKETPLACE_DISABLE_REMOTE",
"ANYCLAW_MARKETPLACE_CACHE_TTL_SECONDS",
"ANYCLAW_MARKETPLACE_REQUEST_TIMEOUT_SECONDS",
"ANYCLAW_MARKETPLACE_AUTO_INSTALL_SKILL",
} {
t.Setenv(key, "")
}
Expand All @@ -70,6 +76,12 @@ func TestDefaultConfig(t *testing.T) {
if cfg.Sandbox.DockerImage == "alpine:3.20" {
t.Fatal("default sandbox docker image should use the bundled sandbox image, not plain Alpine")
}
if cfg.Marketplace.ProtocolVersion != "1.0" {
t.Fatalf("default marketplace protocol = %q, want 1.0", cfg.Marketplace.ProtocolVersion)
}
if cfg.Marketplace.RegistryEndpoint != "" {
t.Fatalf("default marketplace registry endpoint should be empty, got %q", cfg.Marketplace.RegistryEndpoint)
}
}

func TestModularManagerMissingConfigUsesCanonicalDefaults(t *testing.T) {
Expand All @@ -95,6 +107,9 @@ func TestModularManagerMissingConfigUsesCanonicalDefaults(t *testing.T) {
if !reflect.DeepEqual(cfg.Security, defaults.Security) {
t.Fatalf("modular default security drifted: %#v want %#v", cfg.Security, defaults.Security)
}
if !reflect.DeepEqual(cfg.Marketplace, defaults.Marketplace) {
t.Fatalf("modular default marketplace drifted: %#v want %#v", cfg.Marketplace, defaults.Marketplace)
}
}

func TestValidateMissingProvider(t *testing.T) {
Expand Down Expand Up @@ -439,6 +454,44 @@ func TestEnvOverrides(t *testing.T) {
}
}

func TestMarketplaceEnvOverrides(t *testing.T) {
clearConfigEnv(t)
t.Setenv("ANYCLAW_MARKETPLACE_ENDPOINT", "http://127.0.0.1:8791/")
t.Setenv("ANYCLAW_REGISTRY_TOKEN", "registry-token")
t.Setenv("ANYCLAW_MARKETPLACE_DISABLE_REMOTE", "true")
t.Setenv("ANYCLAW_MARKETPLACE_CACHE_TTL_SECONDS", "7")
t.Setenv("ANYCLAW_MARKETPLACE_REQUEST_TIMEOUT_SECONDS", "5")
t.Setenv("ANYCLAW_MARKETPLACE_AUTO_INSTALL_SKILL", "true")

dir := t.TempDir()
path := filepath.Join(dir, "config.json")
data, _ := json.MarshalIndent(DefaultConfig(), "", " ")
os.WriteFile(path, data, 0644)

loaded, err := Load(path)
if err != nil {
t.Fatalf("loading config with marketplace env override should succeed: %v", err)
}
if loaded.Marketplace.RegistryEndpoint != "http://127.0.0.1:8791" {
t.Fatalf("expected normalized marketplace endpoint, got %q", loaded.Marketplace.RegistryEndpoint)
}
if loaded.Marketplace.RegistryToken != "registry-token" {
t.Fatalf("expected registry token from env, got %q", loaded.Marketplace.RegistryToken)
}
if !loaded.Marketplace.DisableRemote {
t.Fatal("expected marketplace remote to be disabled by env")
}
if loaded.Marketplace.CacheTTLSeconds != 7 {
t.Fatalf("expected cache ttl 7, got %d", loaded.Marketplace.CacheTTLSeconds)
}
if loaded.Marketplace.RequestTimeoutSeconds != 5 {
t.Fatalf("expected request timeout 5, got %d", loaded.Marketplace.RequestTimeoutSeconds)
}
if !loaded.Marketplace.AutoInstallSkill {
t.Fatal("expected auto install skill to be enabled by env")
}
}

func TestLoadPersistedSkipsEnvOverrides(t *testing.T) {
clearConfigEnv(t)
t.Setenv("OPENAI_API_KEY", "test-key-123")
Expand Down
8 changes: 8 additions & 0 deletions pkg/config/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,14 @@ func DefaultConfig() *Config {
EnableDecomposition: true,
SubAgents: nil,
},
Marketplace: MarketplaceConfig{
ProtocolVersion: "1.0",
CacheTTLSeconds: 60,
RequestTimeoutSeconds: 30,
DownloadTimeoutSeconds: 300,
RetryCount: 1,
AutoInstallSkill: false,
},
}
}

Expand Down
22 changes: 22 additions & 0 deletions pkg/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,26 @@ func applyEnvOverrides(cfg *Config) {
cfg.Plugins.ExecTimeoutSeconds = sec
}
}
if v := os.Getenv("ANYCLAW_MARKETPLACE_ENDPOINT"); v != "" {
cfg.Marketplace.RegistryEndpoint = v
}
if v := os.Getenv("ANYCLAW_REGISTRY_TOKEN"); v != "" {
cfg.Marketplace.RegistryToken = v
}
if v := os.Getenv("ANYCLAW_MARKETPLACE_DISABLE_REMOTE"); v != "" {
cfg.Marketplace.DisableRemote = strings.EqualFold(v, "1") || strings.EqualFold(v, "true") || strings.EqualFold(v, "yes")
}
if v := os.Getenv("ANYCLAW_MARKETPLACE_CACHE_TTL_SECONDS"); v != "" {
if sec, err := strconv.Atoi(v); err == nil && sec >= 0 {
cfg.Marketplace.CacheTTLSeconds = sec
}
}
if v := os.Getenv("ANYCLAW_MARKETPLACE_REQUEST_TIMEOUT_SECONDS"); v != "" {
if sec, err := strconv.Atoi(v); err == nil && sec > 0 {
cfg.Marketplace.RequestTimeoutSeconds = sec
}
}
if v := os.Getenv("ANYCLAW_MARKETPLACE_AUTO_INSTALL_SKILL"); v != "" {
cfg.Marketplace.AutoInstallSkill = strings.EqualFold(v, "1") || strings.EqualFold(v, "true") || strings.EqualFold(v, "yes")
}
}
18 changes: 18 additions & 0 deletions pkg/config/normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ func normalizeLoadedConfig(cfg *Config) {
cfg.LLM.BaseURL = strings.TrimSpace(cfg.LLM.BaseURL)
cfg.LLM.DefaultProviderRef = strings.TrimSpace(cfg.LLM.DefaultProviderRef)
cfg.LLM.Proxy = strings.TrimSpace(cfg.LLM.Proxy)
cfg.Marketplace.RegistryEndpoint = strings.TrimRight(strings.TrimSpace(cfg.Marketplace.RegistryEndpoint), "/")
cfg.Marketplace.RegistryToken = strings.TrimSpace(cfg.Marketplace.RegistryToken)
cfg.Marketplace.ProtocolVersion = strings.TrimSpace(cfg.Marketplace.ProtocolVersion)
if cfg.Marketplace.ProtocolVersion == "" {
cfg.Marketplace.ProtocolVersion = "1.0"
}
if cfg.Marketplace.CacheTTLSeconds < 0 {
cfg.Marketplace.CacheTTLSeconds = 0
}
if cfg.Marketplace.RequestTimeoutSeconds <= 0 {
cfg.Marketplace.RequestTimeoutSeconds = 30
}
if cfg.Marketplace.DownloadTimeoutSeconds <= 0 {
cfg.Marketplace.DownloadTimeoutSeconds = 300
}
if cfg.Marketplace.RetryCount < 0 {
cfg.Marketplace.RetryCount = 0
}
cfg.Agent.Name = strings.TrimSpace(cfg.Agent.Name)
cfg.Agent.Description = strings.TrimSpace(cfg.Agent.Description)
cfg.Agent.ActiveProfile = strings.TrimSpace(cfg.Agent.ActiveProfile)
Expand Down
Loading
Loading