WIP: Save agent roles integration work before CHORUS rebrand

- Agent roles and coordination features
- Chat API integration testing
- New configuration and workspace management

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-08-01 02:21:11 +10:00
parent 81b473d48f
commit 5978a0b8f5
3713 changed files with 1103925 additions and 59 deletions

View File

@@ -0,0 +1 @@
/madns/madns

View File

@@ -0,0 +1,30 @@
os:
- linux
language: go
go:
- 1.15.x
env:
global:
- GOTFLAGS="-race"
matrix:
- BUILD_DEPTYPE=gomod
# disable travis install
install:
- true
script:
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
cache:
directories:
- $GOPATH/pkg/mod
- /home/travis/.cache/go-build
notifications:
email: false

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Juan Batiz-Benet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,57 @@
# go-multiaddr-dns
> Resolve /dns4, /dns6, and /dnsaddr multiaddrs.
```sh
> madns /dnsaddr/ipfs.io/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
/ip6/2604:a880:1:20::1d9:6001/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
/ip6/fc3d:9a4e:3c96:2fd2:1afa:18fe:8dd2:b602/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
/dns4/jupiter.i.ipfs.io/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
/dns6/jupiter.i.ipfs.io/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx
```
In more detail:
```sh
> madns /dns6/example.net
/ip6/2001:db8::a3
/ip6/2001:db8::a4
...
> madns /dns4/example.net/tcp/443/wss
/ip4/192.0.2.1/tcp/443/wss
/ip4/192.0.2.2/tcp/443/wss
# No-op if it's not a dns-ish address.
> madns /ip4/127.0.0.1/tcp/8080
/ip4/127.0.0.1/tcp/8080
# /dnsaddr resolves by looking up TXT records.
> dig +short TXT _dnsaddr.example.net
"dnsaddr=/ip6/2001:db8::a3/tcp/443/wss/ipfs/Qmfoo"
"dnsaddr=/ip6/2001:db8::a4/tcp/443/wss/ipfs/Qmbar"
"dnsaddr=/ip4/192.0.2.1/tcp/443/wss/ipfs/Qmfoo"
"dnsaddr=/ip4/192.0.2.2/tcp/443/wss/ipfs/Qmbar"
...
# /dnsaddr returns addrs which encapsulate whatever /dnsaddr encapsulates too.
> madns example.net/ipfs/Qmfoo
info: changing query to /dnsaddr/example.net/ipfs/Qmfoo
/ip6/2001:db8::a3/tcp/443/wss/ipfs/Qmfoo
/ip4/192.0.2.1/tcp/443/wss/ipfs/Qmfoo
# TODO -p filters by protocol stacks.
> madns -p /ip6/tcp/wss /dnsaddr/example.net
/ip6/2001:db8::a3/tcp/443/wss/ipfs/Qmfoo
/ip6/2001:db8::a4/tcp/443/wss/ipfs/Qmbar
# TODO -c filters by CIDR
> madns -c /ip4/104.236.76.0/ipcidr/24 /dnsaddr/example.net
/ip4/192.0.2.2/tcp/443/wss/ipfs/Qmbar
```

29
vendor/github.com/multiformats/go-multiaddr-dns/dns.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
package madns
import (
ma "github.com/multiformats/go-multiaddr"
)
// Extracted from source of truth for multicodec codes: https://github.com/multiformats/multicodec
const (
// Deprecated: use ma.P_DNS
P_DNS = ma.P_DNS
// Deprecated: use ma.P_DNS4
P_DNS4 = ma.P_DNS4
// Deprecated: use ma.P_DNS6
P_DNS6 = ma.P_DNS6
// Deprecated: use ma.P_DNSADDR
P_DNSADDR = ma.P_DNSADDR
)
// Deprecated: use ma.ProtocolWithCode(P_DNS)
var DnsProtocol = ma.ProtocolWithCode(P_DNS)
// Deprecated: use ma.ProtocolWithCode(P_DNS4)
var Dns4Protocol = ma.ProtocolWithCode(P_DNS4)
// Deprecated: use ma.ProtocolWithCode(P_DNS6)
var Dns6Protocol = ma.ProtocolWithCode(P_DNS6)
// Deprecated: use ma.ProtocolWithCode(P_DNSADDR)
var DnsaddrProtocol = ma.ProtocolWithCode(P_DNSADDR)

View File

@@ -0,0 +1,31 @@
package madns
import (
"context"
"net"
)
type MockResolver struct {
IP map[string][]net.IPAddr
TXT map[string][]string
}
var _ BasicResolver = (*MockResolver)(nil)
func (r *MockResolver) LookupIPAddr(ctx context.Context, name string) ([]net.IPAddr, error) {
results, ok := r.IP[name]
if ok {
return results, nil
} else {
return []net.IPAddr{}, nil
}
}
func (r *MockResolver) LookupTXT(ctx context.Context, name string) ([]string, error) {
results, ok := r.TXT[name]
if ok {
return results, nil
} else {
return []string{}, nil
}
}

View File

@@ -0,0 +1,281 @@
package madns
import (
"context"
"net"
"strings"
"github.com/miekg/dns"
ma "github.com/multiformats/go-multiaddr"
)
var ResolvableProtocols = []ma.Protocol{DnsaddrProtocol, Dns4Protocol, Dns6Protocol, DnsProtocol}
var DefaultResolver = &Resolver{def: net.DefaultResolver}
const dnsaddrTXTPrefix = "dnsaddr="
// BasicResolver is a low level interface for DNS resolution
type BasicResolver interface {
LookupIPAddr(context.Context, string) ([]net.IPAddr, error)
LookupTXT(context.Context, string) ([]string, error)
}
// Resolver is an object capable of resolving dns multiaddrs by using one or more BasicResolvers;
// it supports custom per domain/TLD resolvers.
// It also implements the BasicResolver interface so that it can act as a custom per domain/TLD
// resolver.
type Resolver struct {
def BasicResolver
custom map[string]BasicResolver
}
var _ BasicResolver = (*Resolver)(nil)
// NewResolver creates a new Resolver instance with the specified options
func NewResolver(opts ...Option) (*Resolver, error) {
r := &Resolver{def: net.DefaultResolver}
for _, opt := range opts {
err := opt(r)
if err != nil {
return nil, err
}
}
return r, nil
}
type Option func(*Resolver) error
// WithDefaultResolver is an option that specifies the default basic resolver,
// which resolves any TLD that doesn't have a custom resolver.
// Defaults to net.DefaultResolver
func WithDefaultResolver(def BasicResolver) Option {
return func(r *Resolver) error {
r.def = def
return nil
}
}
// WithDomainResolver specifies a custom resolver for a domain/TLD.
// Custom resolver selection matches domains left to right, with more specific resolvers
// superseding generic ones.
func WithDomainResolver(domain string, rslv BasicResolver) Option {
return func(r *Resolver) error {
if r.custom == nil {
r.custom = make(map[string]BasicResolver)
}
fqdn := dns.Fqdn(domain)
r.custom[fqdn] = rslv
return nil
}
}
func (r *Resolver) getResolver(domain string) BasicResolver {
fqdn := dns.Fqdn(domain)
// we match left-to-right, with more specific resolvers superseding generic ones.
// So for a domain a.b.c, we will try a.b,c, b.c, c, and fallback to the default if
// there is no match
rslv, ok := r.custom[fqdn]
if ok {
return rslv
}
for i := strings.Index(fqdn, "."); i != -1; i = strings.Index(fqdn, ".") {
fqdn = fqdn[i+1:]
if fqdn == "" {
// the . is the default resolver
break
}
rslv, ok = r.custom[fqdn]
if ok {
return rslv
}
}
return r.def
}
// Resolve resolves a DNS multiaddr.
func (r *Resolver) Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
var results []ma.Multiaddr
for i := 0; maddr != nil; i++ {
var keep ma.Multiaddr
// Find the next dns component.
keep, maddr = ma.SplitFunc(maddr, func(c ma.Component) bool {
switch c.Protocol().Code {
case DnsProtocol.Code, Dns4Protocol.Code, Dns6Protocol.Code, DnsaddrProtocol.Code:
return true
default:
return false
}
})
// Keep everything before the dns component.
if keep != nil {
if len(results) == 0 {
results = []ma.Multiaddr{keep}
} else {
for i, r := range results {
results[i] = r.Encapsulate(keep)
}
}
}
// If the rest is empty, we've hit the end (there _was_ no dns component).
if maddr == nil {
break
}
// split off the dns component.
var resolve *ma.Component
resolve, maddr = ma.SplitFirst(maddr)
proto := resolve.Protocol()
value := resolve.Value()
rslv := r.getResolver(value)
// resolve the dns component
var resolved []ma.Multiaddr
switch proto.Code {
case Dns4Protocol.Code, Dns6Protocol.Code, DnsProtocol.Code:
// The dns, dns4, and dns6 resolver simply resolves each
// dns* component into an ipv4/ipv6 address.
v4only := proto.Code == Dns4Protocol.Code
v6only := proto.Code == Dns6Protocol.Code
// XXX: Unfortunately, go does a pretty terrible job of
// differentiating between IPv6 and IPv4. A v4-in-v6
// AAAA record will _look_ like an A record to us and
// there's nothing we can do about that.
records, err := rslv.LookupIPAddr(ctx, value)
if err != nil {
return nil, err
}
// Convert each DNS record into a multiaddr. If the
// protocol is dns4, throw away any IPv6 addresses. If
// the protocol is dns6, throw away any IPv4 addresses.
for _, r := range records {
var (
rmaddr ma.Multiaddr
err error
)
ip4 := r.IP.To4()
if ip4 == nil {
if v4only {
continue
}
rmaddr, err = ma.NewMultiaddr("/ip6/" + r.IP.String())
} else {
if v6only {
continue
}
rmaddr, err = ma.NewMultiaddr("/ip4/" + ip4.String())
}
if err != nil {
return nil, err
}
resolved = append(resolved, rmaddr)
}
case DnsaddrProtocol.Code:
// The dnsaddr resolver is a bit more complicated. We:
//
// 1. Lookup the dnsaddr txt record on _dnsaddr.DOMAIN.TLD
// 2. Take everything _after_ the `/dnsaddr/DOMAIN.TLD`
// part of the multiaddr.
// 3. Find the dnsaddr records (if any) with suffixes
// matching the result of step 2.
// First, lookup the TXT record
records, err := rslv.LookupTXT(ctx, "_dnsaddr."+value)
if err != nil {
return nil, err
}
// Then, calculate the length of the suffix we're
// looking for.
length := 0
if maddr != nil {
length = addrLen(maddr)
}
for _, r := range records {
// Ignore non dnsaddr TXT records.
if !strings.HasPrefix(r, dnsaddrTXTPrefix) {
continue
}
// Extract and decode the multiaddr.
rmaddr, err := ma.NewMultiaddr(r[len(dnsaddrTXTPrefix):])
if err != nil {
// discard multiaddrs we don't understand.
// XXX: Is this right? It's the best we
// can do for now, really.
continue
}
// If we have a suffix to match on.
if maddr != nil {
// Make sure the new address is at least
// as long as the suffix we're looking
// for.
rmlen := addrLen(rmaddr)
if rmlen < length {
// not long enough.
continue
}
// Matches everything after the /dnsaddr/... with the end of the
// dnsaddr record:
//
// v----------rmlen-----------------v
// /ip4/1.2.3.4/tcp/1234/p2p/QmFoobar
// /p2p/QmFoobar
// ^--(rmlen - length)--^---length--^
if !maddr.Equal(offset(rmaddr, rmlen-length)) {
continue
}
}
resolved = append(resolved, rmaddr)
}
// consumes the rest of the multiaddr as part of the "match" process.
maddr = nil
default:
panic("unreachable")
}
if len(resolved) == 0 {
return nil, nil
} else if len(results) == 0 {
results = resolved
} else {
// We take the cross product here as we don't have any
// better way to represent "ORs" in multiaddrs. For
// example, `/dns/foo.com/p2p-circuit/dns/bar.com` could
// resolve to:
//
// * /ip4/1.1.1.1/p2p-circuit/ip4/2.1.1.1
// * /ip4/1.1.1.1/p2p-circuit/ip4/2.1.1.2
// * /ip4/1.1.1.2/p2p-circuit/ip4/2.1.1.1
// * /ip4/1.1.1.2/p2p-circuit/ip4/2.1.1.2
results = cross(results, resolved)
}
}
return results, nil
}
func (r *Resolver) LookupIPAddr(ctx context.Context, domain string) ([]net.IPAddr, error) {
return r.getResolver(domain).LookupIPAddr(ctx, domain)
}
func (r *Resolver) LookupTXT(ctx context.Context, txt string) ([]string, error) {
return r.getResolver(txt).LookupTXT(ctx, txt)
}

View File

@@ -0,0 +1,57 @@
package madns
import (
"context"
ma "github.com/multiformats/go-multiaddr"
)
func Matches(maddr ma.Multiaddr) (matches bool) {
ma.ForEach(maddr, func(c ma.Component) bool {
switch c.Protocol().Code {
case DnsProtocol.Code, Dns4Protocol.Code, Dns6Protocol.Code, DnsaddrProtocol.Code:
matches = true
}
return !matches
})
return matches
}
func Resolve(ctx context.Context, maddr ma.Multiaddr) ([]ma.Multiaddr, error) {
return DefaultResolver.Resolve(ctx, maddr)
}
// counts the number of components in the multiaddr
func addrLen(maddr ma.Multiaddr) int {
length := 0
ma.ForEach(maddr, func(_ ma.Component) bool {
length++
return true
})
return length
}
// trims `offset` components from the beginning of the multiaddr.
func offset(maddr ma.Multiaddr, offset int) ma.Multiaddr {
_, after := ma.SplitFunc(maddr, func(c ma.Component) bool {
if offset == 0 {
return true
}
offset--
return false
})
return after
}
// takes the cross product of two sets of multiaddrs
//
// assumes `a` is non-empty.
func cross(a, b []ma.Multiaddr) []ma.Multiaddr {
res := make([]ma.Multiaddr, 0, len(a)*len(b))
for _, x := range a {
for _, y := range b {
res = append(res, x.Encapsulate(y))
}
}
return res
}