feat: URL-derived provider keys (hs-<hostname>) + dedup providers sharing same gateway URL

- GatewayProviderKey() derives stable key from gateway URL hostname
  e.g. https://llmgw.h.raczylo.com/v1 → hs-llmgw-h-raczylo-com
- AbsorbDuplicateProviders() merges providers sharing the same baseURL
  into our canonical entry; user-crafted model metadata wins for overlaps
- kilo: renders to opencode.jsonc (primary config), deep-merges providers,
  deletes enabled_providers filter that was hiding our gateway
- opencode: absorbs duplicate providers from existing config
- All adapters use GatewayProviderKey() instead of hardcoded string
- Fix goose URL doubling (was /v1/v1/chat/completions → /chat/completions)
- Fix token propagation: api_key written directly (not env var ref)
- Zed: adds prompt_cache_key capability, deletes context_servers key
  to avoid ContextServerSettingsContent serde untagged-enum parse error
This commit is contained in:
2026-05-23 21:13:16 +01:00
parent 77158d9f2b
commit fb02b165d7
16 changed files with 180 additions and 57 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
<p align="center">
<img src="docs/assets/banner.svg" alt="harness-sync — one config, every harness" width="900">
<a href="https://harness-sync.raczylo.com"><img src="docs/assets/banner.svg" alt="harness-sync — one config, every harness" width="900"></a>
</p>
<p align="center">
+1 -1
View File
@@ -1 +1 @@
harness-sync.raczylo.com
harness-sync.raczylo.com
+10
View File
@@ -8,6 +8,16 @@
name="description"
content="One canonical source of truth for skills, agents, MCP servers, LLM endpoints, and global instructions across every LLM harness on your machine."
/>
<link rel="canonical" href="https://harness-sync.raczylo.com" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://harness-sync.raczylo.com" />
<meta property="og:title" content="harness-sync — One Config, Every Harness" />
<meta property="og:description" content="One canonical source of truth for skills, agents, MCP servers, LLM endpoints, and global instructions across every LLM harness on your machine." />
<meta property="og:image" content="https://harness-sync.raczylo.com/assets/banner.svg" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="harness-sync — One Config, Every Harness" />
<meta name="twitter:description" content="One canonical source of truth for skills, agents, MCP servers, LLM endpoints, and global instructions across every LLM harness on your machine." />
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
+31 -9
View File
@@ -3,15 +3,36 @@ package common
import (
"encoding/json"
"net/url"
"strings"
"github.com/lukaszraczylo/harness-sync/internal/canonical"
)
const gatewayProviderID = "harness-sync-gateway"
const fallbackGatewayProviderID = "harness-sync-gateway"
// GatewayProviderID is the canonical provider key used across all adapters.
const GatewayProviderID = gatewayProviderID
// Deprecated: prefer GatewayProviderKey(gatewayURL) for a stable URL-derived key.
const GatewayProviderID = fallbackGatewayProviderID
// GatewayProviderKey returns a stable provider key derived from the gateway URL.
// Format: "hs-<hostname>" with dots and special chars normalised to dashes.
// Falls back to "harness-sync-gateway" when the URL is empty or unparseable.
//
// https://llmgw.h.raczylo.com/v1 → hs-llmgw-h-raczylo-com
func GatewayProviderKey(gatewayURL string) string {
if gatewayURL == "" {
return fallbackGatewayProviderID
}
u, err := url.Parse(gatewayURL)
if err != nil || u.Hostname() == "" {
return fallbackGatewayProviderID
}
host := u.Hostname()
// Replace dots, underscores, and colons (port separators) with dashes.
r := strings.NewReplacer(".", "-", "_", "-", ":", "-")
return "hs-" + r.Replace(host)
}
// ProviderEntry is a generic provider record. Adapters may massage further.
type ProviderEntry = map[string]any
@@ -22,8 +43,9 @@ type ProviderEntry = map[string]any
func BuildProviders(p *canonical.Profile) []ProviderEntry {
out := make([]ProviderEntry, 0, len(p.Upstreams)+1)
if p.Gateway.URL != "" {
key := GatewayProviderKey(p.Gateway.URL)
gw := ProviderEntry{
"id": gatewayProviderID,
"id": key,
"name": "harness-sync gateway",
"base_url": p.Gateway.URL,
"api_key": p.Gateway.Token,
@@ -89,7 +111,7 @@ func ProvidersAsCrushMap(p *canonical.Profile) map[string]any {
if len(models) == 0 && p.Gateway.DefaultModel != "" {
models = append(models, crushModel(p.Gateway.DefaultModel))
}
out[gatewayProviderID] = map[string]any{
out[GatewayProviderKey(p.Gateway.URL)] = map[string]any{
"type": "openai-compat",
"base_url": p.Gateway.URL,
"api_key": p.Gateway.Token,
@@ -106,7 +128,7 @@ func CrushRoleModels(p *canonical.Profile) map[string]any {
}
sel := map[string]any{
"model": p.Gateway.DefaultModel,
"provider": gatewayProviderID,
"provider": GatewayProviderKey(p.Gateway.URL),
}
return map[string]any{
"large": sel,
@@ -143,7 +165,7 @@ func ProvidersAsMap(p *canonical.Profile) map[string]any {
if len(models) > 0 {
entry["models"] = models
}
out[gatewayProviderID] = entry
out[GatewayProviderKey(p.Gateway.URL)] = entry
}
for _, up := range p.Upstreams {
entry := map[string]any{
@@ -169,7 +191,7 @@ func KiloModelString(p *canonical.Profile) string {
if p.Gateway.DefaultModel == "" {
return ""
}
return gatewayProviderID + "/" + p.Gateway.DefaultModel
return GatewayProviderKey(p.Gateway.URL) + "/" + p.Gateway.DefaultModel
}
// ProvidersAsCagentMap returns the providers block for cagent:
@@ -179,7 +201,7 @@ func ProvidersAsCagentMap(p *canonical.Profile) map[string]any {
if p.Gateway.URL == "" {
return out
}
out[gatewayProviderID] = map[string]any{
out[GatewayProviderKey(p.Gateway.URL)] = map[string]any{
"base_url": p.Gateway.URL,
"api_key": p.Gateway.Token,
"provider": "openai",
@@ -227,7 +249,7 @@ func GooseCustomProviderFile(p *canonical.Profile) ([]byte, string) {
if p.Gateway.URL == "" {
return nil, ""
}
providerID := gatewayProviderID
providerID := GatewayProviderKey(p.Gateway.URL)
providerName := "custom_" + providerID
models := make([]map[string]any, 0)
+1 -1
View File
@@ -19,7 +19,7 @@ func TestBuildProvidersGatewayFirst(t *testing.T) {
}
out := BuildProviders(p)
require.Len(t, out, 2)
assert.Equal(t, "harness-sync-gateway", out[0]["id"])
assert.Equal(t, "hs-gw", out[0]["id"])
assert.Equal(t, "anthropic", out[1]["id"])
assert.NotNil(t, out[0]["models"])
}
+69
View File
@@ -0,0 +1,69 @@
package common
import "maps"
// AbsorbDuplicateProviders scans provMap for entries (other than ourKey) whose
// options.baseURL matches gatewayURL and absorbs their models into our entry.
//
// Merge strategy:
// - Models only in the duplicate: added to our provider.
// - Models only in our canonical entry: kept as-is.
// - Models in both: the DUPLICATE's entry wins (preserves user-crafted display
// names and custom limits that are often more descriptive than the profile
// alias).
//
// Absorbed entries are deleted from the returned map. Returns provMap unchanged
// when gatewayURL is empty or our entry is absent.
func AbsorbDuplicateProviders(provMap map[string]any, ourKey, gatewayURL string) map[string]any {
if gatewayURL == "" || provMap == nil {
return provMap
}
ourEntry, ok := provMap[ourKey].(map[string]any)
if !ok {
return provMap
}
// Clone top-level so we don't mutate the caller's map.
result := make(map[string]any, len(provMap))
maps.Copy(result, provMap)
ourModels, _ := ourEntry["models"].(map[string]any)
if ourModels == nil {
ourModels = map[string]any{}
}
// merged starts with our canonical models; duplicates may override per-model.
merged := make(map[string]any, len(ourModels))
maps.Copy(merged, ourModels)
for k, v := range result {
if k == ourKey {
continue
}
entry, ok := v.(map[string]any)
if !ok {
continue
}
opts, _ := entry["options"].(map[string]any)
if opts == nil {
continue
}
if opts["baseURL"] != gatewayURL {
continue
}
// Same gateway URL — absorb and delete.
if dupModels, ok := entry["models"].(map[string]any); ok {
// Duplicate (user-crafted) wins for overlapping IDs.
maps.Copy(merged, dupModels)
}
delete(result, k)
}
updated := make(map[string]any, len(ourEntry))
maps.Copy(updated, ourEntry)
if len(merged) > 0 {
updated["models"] = merged
}
result[ourKey] = updated
return result
}
+4 -6
View File
@@ -89,11 +89,9 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
mcps[s.Name] = e
}
provKey := common.GatewayProviderKey(b.Profile.Gateway.URL)
// inline model shorthand: "providerID/modelID"
modelRef := ""
if b.Profile.Gateway.DefaultModel != "" {
modelRef = "harness-sync-gateway/" + b.Profile.Gateway.DefaultModel
}
modelRef := common.KiloModelString(&b.Profile)
cfg := map[string]any{
"version": 8,
@@ -108,8 +106,8 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
},
"providers": common.ProvidersAsCagentMap(&b.Profile),
"models": map[string]any{
"harness-sync-gateway": map[string]any{
"provider": "harness-sync-gateway",
provKey: map[string]any{
"provider": provKey,
"model": b.Profile.Gateway.DefaultModel,
},
},
+6 -6
View File
@@ -48,20 +48,20 @@ func TestCagentRenderProducesExpectedTargets(t *testing.T) {
agents := parsed["agents"].(map[string]any)
def := agents["default"].(map[string]any)
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", def["model"])
assert.Equal(t, "hs-gw/claude-sonnet-4-6", def["model"])
assert.Contains(t, def["instruction"], "# global instruction")
// providers map (new schema).
providers := parsed["providers"].(map[string]any)
gwProv := providers["harness-sync-gateway"].(map[string]any)
gwProv := providers["hs-gw"].(map[string]any)
assert.Equal(t, "https://gw", gwProv["base_url"])
assert.Equal(t, "openai", gwProv["provider"])
assert.NotContains(t, gwProv, "token_key")
// models map.
models := parsed["models"].(map[string]any)
gw := models["harness-sync-gateway"].(map[string]any)
assert.Equal(t, "harness-sync-gateway", gw["provider"])
gw := models["hs-gw"].(map[string]any)
assert.Equal(t, "hs-gw", gw["provider"])
assert.Equal(t, "claude-sonnet-4-6", gw["model"])
mcps := parsed["mcps"].(map[string]any)
@@ -93,7 +93,7 @@ func TestCagentRenderProvidersAndModelsMaps(t *testing.T) {
// providers block present.
providers, ok := parsed["providers"].(map[string]any)
require.True(t, ok, "providers must be a map")
gw, ok := providers["harness-sync-gateway"].(map[string]any)
gw, ok := providers["hs-gw"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "https://gw", gw["base_url"])
assert.Equal(t, "tok", gw["api_key"])
@@ -103,7 +103,7 @@ func TestCagentRenderProvidersAndModelsMaps(t *testing.T) {
// agents.default.model uses inline shorthand.
agents := parsed["agents"].(map[string]any)
def := agents["default"].(map[string]any)
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", def["model"])
assert.Equal(t, "hs-gw/claude-sonnet-4-6", def["model"])
}
func TestCagentRenderNoMCP(t *testing.T) {
+4 -4
View File
@@ -44,8 +44,8 @@ func TestCrushRenderProducesExpectedTargets(t *testing.T) {
// providers must be a map (not array), keyed by providerID.
providers, ok := parsed["providers"].(map[string]any)
require.True(t, ok, "providers must be a map")
gw, ok := providers["harness-sync-gateway"].(map[string]any)
require.True(t, ok, "harness-sync-gateway provider missing")
gw, ok := providers["hs-gw"].(map[string]any)
require.True(t, ok, "hs-gw provider missing")
assert.Equal(t, "openai-compat", gw["type"])
// models must be role-map with large/small/title.
models, ok := parsed["models"].(map[string]any)
@@ -137,7 +137,7 @@ func TestCrushRenderProducesMapProvidersAndRoleModels(t *testing.T) {
// providers is a map, not an array.
providers, ok := parsed["providers"].(map[string]any)
require.True(t, ok, "providers must be a map")
gw, ok := providers["harness-sync-gateway"].(map[string]any)
gw, ok := providers["hs-gw"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "openai-compat", gw["type"])
assert.Equal(t, "https://gw", gw["base_url"])
@@ -160,7 +160,7 @@ func TestCrushRenderProducesMapProvidersAndRoleModels(t *testing.T) {
sel, ok := roleModels[role].(map[string]any)
require.True(t, ok, "role %s missing", role)
assert.Equal(t, "claude-sonnet-4-6", sel["model"])
assert.Equal(t, "harness-sync-gateway", sel["provider"])
assert.Equal(t, "hs-gw", sel["provider"])
}
}
+3 -3
View File
@@ -91,7 +91,7 @@ func TestGooseRenderGatewayURL(t *testing.T) {
var parsed map[string]any
require.NoError(t, yaml.Unmarshal(seen[cfgDest].Content, &parsed))
// Custom gateway → GOOSE_PROVIDER is the custom provider name.
assert.Equal(t, "custom_harness-sync-gateway", parsed["GOOSE_PROVIDER"])
assert.Equal(t, "custom_hs-gw", parsed["GOOSE_PROVIDER"])
}
func TestGooseRenderProducesCustomProviderFile(t *testing.T) {
@@ -109,13 +109,13 @@ func TestGooseRenderProducesCustomProviderFile(t *testing.T) {
seen := map[string]adapter.File{}
fs.ForEach(func(f adapter.File) { seen[f.Dest] = f })
cpDest := filepath.Join(home, ".config", "goose", "custom_providers", "custom_harness-sync-gateway.json")
cpDest := filepath.Join(home, ".config", "goose", "custom_providers", "custom_hs-gw.json")
require.Contains(t, seen, cpDest)
assert.Equal(t, adapter.RenderedFile, seen[cpDest].Kind)
var cp map[string]any
require.NoError(t, json.Unmarshal(seen[cpDest].Content, &cp))
assert.Equal(t, "custom_harness-sync-gateway", cp["name"])
assert.Equal(t, "custom_hs-gw", cp["name"])
assert.Equal(t, "openai", cp["engine"])
assert.Equal(t, "tok", cp["api_key"])
assert.NotContains(t, cp, "api_key_env")
+10 -6
View File
@@ -126,14 +126,18 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
"enabled_providers": nil, // delete filter
}
// Deep-merge provider: add our entries INTO the existing provider map.
// Deep-merge provider: add our entries INTO the existing provider map,
// then absorb any duplicate providers that share the same gateway URL.
if newProv, ok := overlay["provider"].(map[string]any); ok && len(newProv) > 0 {
existing, _ := ocBase["provider"].(map[string]any)
if existing == nil {
existing = map[string]any{}
existingProv, _ := ocBase["provider"].(map[string]any)
if existingProv == nil {
existingProv = map[string]any{}
}
maps.Copy(existing, newProv)
ocOverlay["provider"] = existing
maps.Copy(existingProv, newProv)
// Absorb providers at the same URL into harness-sync-gateway,
// preserving user-crafted display names and custom limits.
merged := common.AbsorbDuplicateProviders(existingProv, common.GatewayProviderKey(b.Profile.Gateway.URL), b.Profile.Gateway.URL)
ocOverlay["provider"] = merged
}
// Copy model/small_model/mcp from the kilo.json overlay.
for _, k := range []string{"model", "small_model", "mcp"} {
+9 -9
View File
@@ -43,12 +43,12 @@ func TestKiloRender(t *testing.T) {
var parsed map[string]any
require.NoError(t, json.Unmarshal(seen[cfgDest].Content, &parsed))
// model is "providerID/modelID" string.
assert.Equal(t, "harness-sync-gateway/sonnet", parsed["model"])
assert.Equal(t, "harness-sync-gateway/sonnet", parsed["small_model"])
assert.Equal(t, "hs-gw/sonnet", parsed["model"])
assert.Equal(t, "hs-gw/sonnet", parsed["small_model"])
// provider map present with npm field.
provider, ok := parsed["provider"].(map[string]any)
require.True(t, ok, "provider must be a map")
gw, ok := provider["harness-sync-gateway"].(map[string]any)
gw, ok := provider["hs-gw"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "@ai-sdk/openai-compatible", gw["npm"])
assert.Contains(t, parsed, "mcp")
@@ -113,14 +113,14 @@ func TestKiloRenderEmitsModelString(t *testing.T) {
var parsed map[string]any
require.NoError(t, json.Unmarshal(seen[cfgDest].Content, &parsed))
// model must be "harness-sync-gateway/<modelID>".
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", parsed["model"])
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", parsed["small_model"])
// model must be "hs-gw/<modelID>".
assert.Equal(t, "hs-gw/claude-sonnet-4-6", parsed["model"])
assert.Equal(t, "hs-gw/claude-sonnet-4-6", parsed["small_model"])
// provider map has npm field.
provider, ok := parsed["provider"].(map[string]any)
require.True(t, ok)
gw, ok := provider["harness-sync-gateway"].(map[string]any)
gw, ok := provider["hs-gw"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "@ai-sdk/openai-compatible", gw["npm"])
opts, ok := gw["options"].(map[string]any)
@@ -165,9 +165,9 @@ func TestKiloRenderMergesOpenCodeJSONC(t *testing.T) {
// llmgw preserved, harness-sync-gateway added.
provider := parsed["provider"].(map[string]any)
assert.Contains(t, provider, "llmgw")
assert.Contains(t, provider, "harness-sync-gateway")
assert.Contains(t, provider, "hs-gw")
// model updated.
assert.Equal(t, "harness-sync-gateway/sonnet", parsed["model"])
assert.Equal(t, "hs-gw/sonnet", parsed["model"])
}
func TestKiloImport(t *testing.T) {
+21 -1
View File
@@ -2,6 +2,7 @@
package opencode
import (
"encoding/json"
"os"
"path/filepath"
@@ -76,8 +77,27 @@ func (a *Adapter) Render(b *canonical.Bundle) (*adapter.FileSet, error) {
cfgPath := filepath.Join(base, "opencode.jsonc")
existing, _ := os.ReadFile(cfgPath)
// Build the provider map, then absorb any existing providers at the same
// gateway URL so duplicates don't accumulate across apply runs.
provMap := common.ProvidersAsMap(&b.Profile)
if len(existing) > 0 {
var base map[string]any
clean := common.StripJSONComments(string(existing))
if json.Unmarshal([]byte(clean), &base) == nil {
if existingProv, ok := base["provider"].(map[string]any); ok {
for k, v := range existingProv {
if _, ours := provMap[k]; !ours {
provMap[k] = v
}
}
provMap = common.AbsorbDuplicateProviders(provMap, common.GatewayProviderKey(b.Profile.Gateway.URL), b.Profile.Gateway.URL)
}
}
}
overlay := map[string]any{
"provider": common.ProvidersAsMap(&b.Profile),
"provider": provMap,
"model": common.KiloModelString(&b.Profile),
}
if mcp := common.BuildMCPMapStyled(&b.MCP, common.MCPOpencodeStyle); len(mcp) > 0 {
+3 -3
View File
@@ -129,15 +129,15 @@ func TestOpencodeRenderProviderHasNpm(t *testing.T) {
// provider map has npm field.
provider, ok := parsed["provider"].(map[string]any)
require.True(t, ok)
gw, ok := provider["harness-sync-gateway"].(map[string]any)
gw, ok := provider["hs-gw"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "@ai-sdk/openai-compatible", gw["npm"])
opts, ok := gw["options"].(map[string]any)
require.True(t, ok)
assert.Equal(t, "https://gw", opts["baseURL"])
// model is "harness-sync-gateway/modelID".
assert.Equal(t, "harness-sync-gateway/claude-sonnet-4-6", parsed["model"])
// model is "hs-gw/modelID".
assert.Equal(t, "hs-gw/claude-sonnet-4-6", parsed["model"])
}
func TestOpencodeImportStripsComments(t *testing.T) {
+2 -2
View File
@@ -136,7 +136,7 @@ func zedLanguageModels(p *canonical.Profile) map[string]any {
}
return map[string]any{
"openai_compatible": map[string]any{
common.GatewayProviderID: entry,
common.GatewayProviderKey(p.Gateway.URL): entry,
},
}
}
@@ -156,7 +156,7 @@ func zedAgentBlock(existing []byte, p *canonical.Profile) map[string]any {
}
if p.Gateway.DefaultModel != "" {
base["default_model"] = map[string]any{
"provider": common.GatewayProviderID,
"provider": common.GatewayProviderKey(p.Gateway.URL),
"model": p.Gateway.DefaultModel,
}
}
+5 -5
View File
@@ -46,16 +46,16 @@ func TestZedRenderProducesExpectedTargets(t *testing.T) {
var parsed map[string]any
require.NoError(t, json.Unmarshal(seen[cfgDest].Content, &parsed))
// agent.default_model routes through harness-sync-gateway (openai_compatible provider).
// agent.default_model routes through hs-llmgw-example-com (openai_compatible provider).
agent := parsed["agent"].(map[string]any)
dm := agent["default_model"].(map[string]any)
assert.Equal(t, "harness-sync-gateway", dm["provider"])
assert.Equal(t, "hs-llmgw-example-com", dm["provider"])
assert.Equal(t, "claude-sonnet-4-6", dm["model"])
// language_models.openai_compatible has our named gateway entry.
lm := parsed["language_models"].(map[string]any)
oc := lm["openai_compatible"].(map[string]any)
gw := oc["harness-sync-gateway"].(map[string]any)
gw := oc["hs-llmgw-example-com"].(map[string]any)
assert.Equal(t, "https://llmgw.example.com/v1", gw["api_url"])
// context_servers must be absent — Zed serde untagged-enum bug aborts
@@ -140,8 +140,8 @@ func TestZedRenderEmitsLanguageModelsOpenAICompatible(t *testing.T) {
require.True(t, ok, "language_models must be present")
oc, ok := lm["openai_compatible"].(map[string]any)
require.True(t, ok, "language_models.openai_compatible must be present")
gw, ok := oc["harness-sync-gateway"].(map[string]any)
require.True(t, ok, "openai_compatible.harness-sync-gateway must be present")
gw, ok := oc["hs-gw"].(map[string]any)
require.True(t, ok, "openai_compatible.hs-gw must be present")
assert.Equal(t, "https://gw", gw["api_url"])
avail, ok := gw["available_models"].([]any)
require.True(t, ok)