Allow zotfiles from any GitHub owner
- commit
- f57d176db0afb48edb77178b1a3894ad87fe418f
- author
- patriceckhart · · 2026-08-03 17:26 UTC
- parent
- 6bae491
- signature
- unsigned
4 files · +39 -40
M README.md +3 -3
| @@ -249,11 +249,11 @@ A zotfile packages an agent's instructions, skills, requirements, and enforced t | |||
| 249 | 249 | ```bash | |
| 250 | 250 | zot run ./my-agent | |
| 251 | 251 | zot run ./my-agent.zot | |
| 252 | - | zot run zot-maintenance --cwd /path/to/zot | |
| 253 | - | zot run https://github.com/patriceckhart/agents/zot-maintenance --cwd /path/to/zot | |
| 252 | + | zot run frkr/zot-archify | |
| 253 | + | zot run acme/agents/code-reviewer --cwd /path/to/project | |
| 254 | 254 | ``` | |
| 255 | 255 | ||
| 256 | - | Short names resolve local-first: zot checks the matching directory and `.zot` archive before falling back to the official GitHub agent collection. For GitHub sources, zot downloads the repository archive into a temporary directory, validates and runs the selected agent, then removes the downloaded source when the command exits. Agent data, consent receipts, and sessions still persist under `$ZOT_HOME`. See [docs/zotfiles.md](docs/zotfiles.md) for authoring, permissions, packaging, and current limitations. | |
| 256 | + | Single-part names resolve only to a matching local directory or `.zot` archive. Anyone can publish an agent as a public GitHub repository and run it as `owner/repository`, or publish a collection and run an agent directory as `owner/repository/agent`. zot has no built-in owner, official collection, configured registry, or allowlist. For GitHub sources, zot downloads the repository archive into a temporary directory, validates and runs the selected agent, then removes the downloaded source when the command exits. Agent data, consent receipts, and sessions still persist under `$ZOT_HOME`. See [docs/zotfiles.md](docs/zotfiles.md) for authoring, permissions, packaging, and current limitations. | |
| 257 | 257 | ||
| 258 | 258 | ## Embedding | |
| 259 | 259 | ||
| @@ -249,11 +249,11 @@ A zotfile packages an agent's instructions, skills, requirements, and enforced t | |||||
| 249 | ```bash | 249 | ```bash | ||
| 250 | zot run ./my-agent | 250 | zot run ./my-agent | ||
| 251 | zot run ./my-agent.zot | 251 | zot run ./my-agent.zot | ||
| 252 | - | zot run zot-maintenance --cwd /path/to/zot | 252 | + | zot run frkr/zot-archify |
| 253 | - | zot run https://github.com/patriceckhart/agents/zot-maintenance --cwd /path/to/zot | 253 | + | zot run acme/agents/code-reviewer --cwd /path/to/project |
| 254 | ``` | 254 | ``` | ||
| 255 | 255 | ||||
| 256 | - | Short names resolve local-first: zot checks the matching directory and `.zot` archive before falling back to the official GitHub agent collection. For GitHub sources, zot downloads the repository archive into a temporary directory, validates and runs the selected agent, then removes the downloaded source when the command exits. Agent data, consent receipts, and sessions still persist under `$ZOT_HOME`. See [docs/zotfiles.md](docs/zotfiles.md) for authoring, permissions, packaging, and current limitations. | 256 | + | Single-part names resolve only to a matching local directory or `.zot` archive. Anyone can publish an agent as a public GitHub repository and run it as `owner/repository`, or publish a collection and run an agent directory as `owner/repository/agent`. zot has no built-in owner, official collection, configured registry, or allowlist. For GitHub sources, zot downloads the repository archive into a temporary directory, validates and runs the selected agent, then removes the downloaded source when the command exits. Agent data, consent receipts, and sessions still persist under `$ZOT_HOME`. See [docs/zotfiles.md](docs/zotfiles.md) for authoring, permissions, packaging, and current limitations. |
| 257 | 257 | ||||
| 258 | ## Embedding | 258 | ## Embedding | ||
| 259 | 259 | ||||
M docs/zotfiles.md +15 -17
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | A zotfile packages an agent's behavior into one portable `.zot` file. It can contain the agent's instructions, reusable skills, static assets, and metadata describing the runtime, model, operating-system, binary, and tool permissions it needs. | |
| 4 | 4 | ||
| 5 | - | The current implementation supports creating, inspecting, verifying, and running local directories and `.zot` archives. It can also run an agent directory directly from a public GitHub repository without keeping a clone. Short names resolve local-first and then fall back to zot's official GitHub agent collection. Indexed registry distribution, installation, signatures, bundled executable extensions, network permissions, and environment permissions are not implemented yet. | |
| 5 | + | The current implementation supports creating, inspecting, verifying, and running local directories and `.zot` archives. It can also run an agent repository or directory directly from any public GitHub repository without keeping a clone. zot has no built-in owner or official collection. Indexed registry distribution, installation, signatures, bundled executable extensions, network permissions, and environment permissions are not implemented yet. | |
| 6 | 6 | ||
| 7 | 7 | ## Quick start | |
| 8 | 8 | ||
| @@ -513,44 +513,42 @@ zot run ./my-agent.zot --json "Do the task" | |||
| 513 | 513 | ||
| 514 | 514 | Arguments after the reference use zot's normal CLI parser, so model, provider, reasoning, cwd, session, tool, and output-mode flags remain available. The manifest still imposes its permission and compatibility ceiling. In interactive mode, enabling **show loaded resources at startup** in `/settings` displays the manifest name in an `[Agent]` section above the transcript. | |
| 515 | 515 | ||
| 516 | - | Local filesystem directories, local archive paths, short names, and public GitHub agent-directory URLs are accepted. A short name uses local-first resolution: | |
| 516 | + | Local filesystem directories, local archive paths, GitHub shorthand, and public GitHub agent-directory URLs are accepted. A single-part name resolves only to a matching local directory or `.zot` archive: | |
| 517 | 517 | ||
| 518 | 518 | ```bash | |
| 519 | - | zot run zot-maintenance | |
| 519 | + | zot run my-agent | |
| 520 | 520 | ``` | |
| 521 | 521 | ||
| 522 | - | Zot checks `./zot-maintenance` and `./zot-maintenance.zot` before falling back to `https://github.com/patriceckhart/agents/zot-maintenance`. This makes local development override the remote collection without an install or cache step. | |
| 523 | - | ||
| 524 | - | A two-part name is treated as a GitHub `owner/repo` reference. Resolution is local-first, then the repository root: | |
| 522 | + | A two-part name is treated as a GitHub `owner/repository` reference. Resolution remains local-first, then uses the repository root: | |
| 525 | 523 | ||
| 526 | 524 | ```bash | |
| 527 | 525 | zot run frkr/zot-archify | |
| 528 | 526 | ``` | |
| 529 | 527 | ||
| 530 | - | The legacy `agents/<name>` form remains reserved for selecting an agent directory from the official `patriceckhart/agents` collection: | |
| 528 | + | Use three or more parts to select an agent directory from any public GitHub collection: | |
| 531 | 529 | ||
| 532 | 530 | ```bash | |
| 533 | - | zot run agents/zot-maintenance | |
| 534 | - | zot run agents\zot-maintenance # also accepted on Windows | |
| 531 | + | zot run acme/agents/reviewer | |
| 532 | + | zot run acme/clankers/reviewers/go | |
| 535 | 533 | ``` | |
| 536 | 534 | ||
| 537 | - | Both parts must use lowercase letters, digits, dots, hyphens, or underscores. Prefix a reference with `./`, use an absolute path, retain the `.zot` suffix, or provide a complete GitHub URL when remote fallback is not wanted. | |
| 535 | + | There is no built-in owner, official collection, collection configuration, or allowlist. Every shorthand part must use lowercase letters, digits, dots, hyphens, or underscores. Backslashes are also accepted as separators on Windows. Prefix a reference with `./`, use an absolute path, retain the `.zot` suffix, or provide a complete GitHub URL to force local or explicit remote resolution. | |
| 538 | 536 | ||
| 539 | 537 | For GitHub, zot downloads the repository archive into a temporary directory, selects the requested agent subdirectory, validates it, runs it, and removes the downloaded files when the command exits: | |
| 540 | 538 | ||
| 541 | 539 | ```bash | |
| 542 | - | zot run https://github.com/patriceckhart/agents/zot-maintenance --cwd /path/to/zot | |
| 540 | + | zot run https://github.com/acme/agents/reviewer | |
| 543 | 541 | ``` | |
| 544 | 542 | ||
| 545 | - | Both the legacy official-collection form above and a standard GitHub tree URL are supported: | |
| 543 | + | A standard GitHub tree URL is also supported: | |
| 546 | 544 | ||
| 547 | 545 | ```bash | |
| 548 | - | zot run https://github.com/patriceckhart/agents/tree/main/zot-maintenance | |
| 546 | + | zot run https://github.com/acme/agents/tree/main/reviewer | |
| 549 | 547 | ``` | |
| 550 | 548 | ||
| 551 | 549 | Short forms read the repository's default branch through GitHub's `HEAD` archive. A tree URL uses the branch or tag in the URL. Private repositories and branch or tag names containing `/` are not currently supported. The downloaded source is temporary, but normal agent data, consent receipts, and session transcripts remain under `$ZOT_HOME`. | |
| 552 | 550 | ||
| 553 | - | Short-name and `owner/repo` fallbacks are direct GitHub mappings, not an indexed or signed registry. Installed names, arbitrary URLs, OCI references, and third-party registry configuration are not resolved yet. | |
| 551 | + | `owner/repository` and `owner/repository/path` are direct GitHub mappings, not an indexed or signed registry. Installed names, arbitrary URLs, and OCI references are not resolved yet. | |
| 554 | 552 | ||
| 555 | 553 | ### `zot pack` | |
| 556 | 554 | ||
| @@ -575,7 +573,7 @@ Packing validates the manifest and directory, then creates a zstd-compressed tar | |||
| 575 | 573 | ```bash | |
| 576 | 574 | zot inspect ./my-agent | |
| 577 | 575 | zot inspect ./my-agent.zot | |
| 578 | - | zot inspect https://github.com/patriceckhart/agents/zot-maintenance | |
| 576 | + | zot inspect https://github.com/acme/agents/code-reviewer | |
| 579 | 577 | ``` | |
| 580 | 578 | ||
| 581 | 579 | Inspection validates and prints the agent's name, version, description, digest, declared permissions, and complete file list. It does not execute the agent. | |
| @@ -620,8 +618,8 @@ Archive extraction rejects absolute paths and parent traversal. Unsupported tar | |||
| 620 | 618 | Implemented now: | |
| 621 | 619 | ||
| 622 | 620 | - local directories and `.zot` archives | |
| 623 | - | - local-first short-name resolution to the official GitHub agent collection | |
| 624 | - | - local-first `owner/repo` resolution to `https://github.com/owner/repo` | |
| 621 | + | - local-only resolution for single-part names | |
| 622 | + | - local-first `owner/repository` and `owner/repository/path` resolution to any public GitHub repository | |
| 625 | 623 | - temporary execution of agent directories from public GitHub repositories | |
| 626 | 624 | - `zot pack`, `zot inspect`, `zot verify`, and `zot run` | |
| 627 | 625 | - canonical tar creation with zstd compression | |
| @@ -2,7 +2,7 @@ | |||||
| 2 | 2 | ||||
| 3 | A zotfile packages an agent's behavior into one portable `.zot` file. It can contain the agent's instructions, reusable skills, static assets, and metadata describing the runtime, model, operating-system, binary, and tool permissions it needs. | 3 | A zotfile packages an agent's behavior into one portable `.zot` file. It can contain the agent's instructions, reusable skills, static assets, and metadata describing the runtime, model, operating-system, binary, and tool permissions it needs. | ||
| 4 | 4 | ||||
| 5 | - | The current implementation supports creating, inspecting, verifying, and running local directories and `.zot` archives. It can also run an agent directory directly from a public GitHub repository without keeping a clone. Short names resolve local-first and then fall back to zot's official GitHub agent collection. Indexed registry distribution, installation, signatures, bundled executable extensions, network permissions, and environment permissions are not implemented yet. | 5 | + | The current implementation supports creating, inspecting, verifying, and running local directories and `.zot` archives. It can also run an agent repository or directory directly from any public GitHub repository without keeping a clone. zot has no built-in owner or official collection. Indexed registry distribution, installation, signatures, bundled executable extensions, network permissions, and environment permissions are not implemented yet. |
| 6 | 6 | ||||
| 7 | ## Quick start | 7 | ## Quick start | ||
| 8 | 8 | ||||
| @@ -513,44 +513,42 @@ zot run ./my-agent.zot --json "Do the task" | |||||
| 513 | 513 | ||||
| 514 | Arguments after the reference use zot's normal CLI parser, so model, provider, reasoning, cwd, session, tool, and output-mode flags remain available. The manifest still imposes its permission and compatibility ceiling. In interactive mode, enabling **show loaded resources at startup** in `/settings` displays the manifest name in an `[Agent]` section above the transcript. | 514 | Arguments after the reference use zot's normal CLI parser, so model, provider, reasoning, cwd, session, tool, and output-mode flags remain available. The manifest still imposes its permission and compatibility ceiling. In interactive mode, enabling **show loaded resources at startup** in `/settings` displays the manifest name in an `[Agent]` section above the transcript. | ||
| 515 | 515 | ||||
| 516 | - | Local filesystem directories, local archive paths, short names, and public GitHub agent-directory URLs are accepted. A short name uses local-first resolution: | 516 | + | Local filesystem directories, local archive paths, GitHub shorthand, and public GitHub agent-directory URLs are accepted. A single-part name resolves only to a matching local directory or `.zot` archive: |
| 517 | 517 | ||||
| 518 | ```bash | 518 | ```bash | ||
| 519 | - | zot run zot-maintenance | 519 | + | zot run my-agent |
| 520 | ``` | 520 | ``` | ||
| 521 | 521 | ||||
| 522 | - | Zot checks `./zot-maintenance` and `./zot-maintenance.zot` before falling back to `https://github.com/patriceckhart/agents/zot-maintenance`. This makes local development override the remote collection without an install or cache step. | 522 | + | A two-part name is treated as a GitHub `owner/repository` reference. Resolution remains local-first, then uses the repository root: |
| 523 | - | ||||
| 524 | - | A two-part name is treated as a GitHub `owner/repo` reference. Resolution is local-first, then the repository root: | |||
| 525 | 523 | ||||
| 526 | ```bash | 524 | ```bash | ||
| 527 | zot run frkr/zot-archify | 525 | zot run frkr/zot-archify | ||
| 528 | ``` | 526 | ``` | ||
| 529 | 527 | ||||
| 530 | - | The legacy `agents/<name>` form remains reserved for selecting an agent directory from the official `patriceckhart/agents` collection: | 528 | + | Use three or more parts to select an agent directory from any public GitHub collection: |
| 531 | 529 | ||||
| 532 | ```bash | 530 | ```bash | ||
| 533 | - | zot run agents/zot-maintenance | 531 | + | zot run acme/agents/reviewer |
| 534 | - | zot run agents\zot-maintenance # also accepted on Windows | 532 | + | zot run acme/clankers/reviewers/go |
| 535 | ``` | 533 | ``` | ||
| 536 | 534 | ||||
| 537 | - | Both parts must use lowercase letters, digits, dots, hyphens, or underscores. Prefix a reference with `./`, use an absolute path, retain the `.zot` suffix, or provide a complete GitHub URL when remote fallback is not wanted. | 535 | + | There is no built-in owner, official collection, collection configuration, or allowlist. Every shorthand part must use lowercase letters, digits, dots, hyphens, or underscores. Backslashes are also accepted as separators on Windows. Prefix a reference with `./`, use an absolute path, retain the `.zot` suffix, or provide a complete GitHub URL to force local or explicit remote resolution. |
| 538 | 536 | ||||
| 539 | For GitHub, zot downloads the repository archive into a temporary directory, selects the requested agent subdirectory, validates it, runs it, and removes the downloaded files when the command exits: | 537 | For GitHub, zot downloads the repository archive into a temporary directory, selects the requested agent subdirectory, validates it, runs it, and removes the downloaded files when the command exits: | ||
| 540 | 538 | ||||
| 541 | ```bash | 539 | ```bash | ||
| 542 | - | zot run https://github.com/patriceckhart/agents/zot-maintenance --cwd /path/to/zot | 540 | + | zot run https://github.com/acme/agents/reviewer |
| 543 | ``` | 541 | ``` | ||
| 544 | 542 | ||||
| 545 | - | Both the legacy official-collection form above and a standard GitHub tree URL are supported: | 543 | + | A standard GitHub tree URL is also supported: |
| 546 | 544 | ||||
| 547 | ```bash | 545 | ```bash | ||
| 548 | - | zot run https://github.com/patriceckhart/agents/tree/main/zot-maintenance | 546 | + | zot run https://github.com/acme/agents/tree/main/reviewer |
| 549 | ``` | 547 | ``` | ||
| 550 | 548 | ||||
| 551 | Short forms read the repository's default branch through GitHub's `HEAD` archive. A tree URL uses the branch or tag in the URL. Private repositories and branch or tag names containing `/` are not currently supported. The downloaded source is temporary, but normal agent data, consent receipts, and session transcripts remain under `$ZOT_HOME`. | 549 | Short forms read the repository's default branch through GitHub's `HEAD` archive. A tree URL uses the branch or tag in the URL. Private repositories and branch or tag names containing `/` are not currently supported. The downloaded source is temporary, but normal agent data, consent receipts, and session transcripts remain under `$ZOT_HOME`. | ||
| 552 | 550 | ||||
| 553 | - | Short-name and `owner/repo` fallbacks are direct GitHub mappings, not an indexed or signed registry. Installed names, arbitrary URLs, OCI references, and third-party registry configuration are not resolved yet. | 551 | + | `owner/repository` and `owner/repository/path` are direct GitHub mappings, not an indexed or signed registry. Installed names, arbitrary URLs, and OCI references are not resolved yet. |
| 554 | 552 | ||||
| 555 | ### `zot pack` | 553 | ### `zot pack` | ||
| 556 | 554 | ||||
| @@ -575,7 +573,7 @@ Packing validates the manifest and directory, then creates a zstd-compressed tar | |||||
| 575 | ```bash | 573 | ```bash | ||
| 576 | zot inspect ./my-agent | 574 | zot inspect ./my-agent | ||
| 577 | zot inspect ./my-agent.zot | 575 | zot inspect ./my-agent.zot | ||
| 578 | - | zot inspect https://github.com/patriceckhart/agents/zot-maintenance | 576 | + | zot inspect https://github.com/acme/agents/code-reviewer |
| 579 | ``` | 577 | ``` | ||
| 580 | 578 | ||||
| 581 | Inspection validates and prints the agent's name, version, description, digest, declared permissions, and complete file list. It does not execute the agent. | 579 | Inspection validates and prints the agent's name, version, description, digest, declared permissions, and complete file list. It does not execute the agent. | ||
| @@ -620,8 +618,8 @@ Archive extraction rejects absolute paths and parent traversal. Unsupported tar | |||||
| 620 | Implemented now: | 618 | Implemented now: | ||
| 621 | 619 | ||||
| 622 | - local directories and `.zot` archives | 620 | - local directories and `.zot` archives | ||
| 623 | - | - local-first short-name resolution to the official GitHub agent collection | 621 | + | - local-only resolution for single-part names |
| 624 | - | - local-first `owner/repo` resolution to `https://github.com/owner/repo` | 622 | + | - local-first `owner/repository` and `owner/repository/path` resolution to any public GitHub repository |
| 625 | - temporary execution of agent directories from public GitHub repositories | 623 | - temporary execution of agent directories from public GitHub repositories | ||
| 626 | - `zot pack`, `zot inspect`, `zot verify`, and `zot run` | 624 | - `zot pack`, `zot inspect`, `zot verify`, and `zot run` | ||
| 627 | - canonical tar creation with zstd compression | 625 | - canonical tar creation with zstd compression | ||
M packages/agent/zotfile.go +11 -13
| @@ -383,8 +383,6 @@ func loadZotfile(ref string) (zotfileLoaded, func(), error) { | |||
| 383 | 383 | return zotfileLoaded{Dir: tmp, Temp: true, Digest: digest, Manifest: m}, cleanup, nil | |
| 384 | 384 | } | |
| 385 | 385 | ||
| 386 | - | const officialZotfileCollection = "https://github.com/patriceckhart/agents" | |
| 387 | - | ||
| 388 | 386 | func resolveZotfileRef(ref string) (string, error) { | |
| 389 | 387 | if u, err := url.Parse(ref); err == nil && (u.Scheme == "http" || u.Scheme == "https") { | |
| 390 | 388 | return ref, nil | |
| @@ -405,23 +403,23 @@ func resolveZotfileRef(ref string) (string, error) { | |||
| 405 | 403 | if strings.HasSuffix(strings.ToLower(ref), ".zot") { | |
| 406 | 404 | return ref, nil | |
| 407 | 405 | } | |
| 408 | - | collectionRef := strings.ReplaceAll(ref, "\\", "/") | |
| 409 | - | parts := strings.Split(collectionRef, "/") | |
| 410 | - | if len(parts) == 1 && validZotfileCollectionSegment(parts[0]) { | |
| 411 | - | return officialZotfileCollection + "/" + parts[0], nil | |
| 406 | + | githubRef := strings.ReplaceAll(ref, "\\", "/") | |
| 407 | + | parts := strings.Split(githubRef, "/") | |
| 408 | + | if len(parts) == 2 && validGitHubZotfileSegment(parts[0]) && validGitHubZotfileSegment(parts[1]) { | |
| 409 | + | return "https://github.com/" + parts[0] + "/" + parts[1], nil | |
| 412 | 410 | } | |
| 413 | - | if len(parts) == 2 && validZotfileCollectionSegment(parts[0]) && validZotfileCollectionSegment(parts[1]) { | |
| 414 | - | // Preserve the original official-collection shorthand, where | |
| 415 | - | // agents/<name> selects a subdirectory of patriceckhart/agents. | |
| 416 | - | if parts[0] == "agents" { | |
| 417 | - | return officialZotfileCollection + "/" + parts[1], nil | |
| 411 | + | if len(parts) >= 3 { | |
| 412 | + | for _, part := range parts { | |
| 413 | + | if !validGitHubZotfileSegment(part) { | |
| 414 | + | return ref, nil | |
| 415 | + | } | |
| 418 | 416 | } | |
| 419 | - | return "https://github.com/" + parts[0] + "/" + parts[1], nil | |
| 417 | + | return "https://github.com/" + strings.Join(parts, "/"), nil | |
| 420 | 418 | } | |
| 421 | 419 | return ref, nil | |
| 422 | 420 | } | |
| 423 | 421 | ||
| 424 | - | func validZotfileCollectionSegment(s string) bool { | |
| 422 | + | func validGitHubZotfileSegment(s string) bool { | |
| 425 | 423 | return s != "" && s == strings.ToLower(s) && safeAgentName(s) == s | |
| 426 | 424 | } | |
| 427 | 425 | ||
| @@ -383,8 +383,6 @@ func loadZotfile(ref string) (zotfileLoaded, func(), error) { | |||||
| 383 | return zotfileLoaded{Dir: tmp, Temp: true, Digest: digest, Manifest: m}, cleanup, nil | 383 | return zotfileLoaded{Dir: tmp, Temp: true, Digest: digest, Manifest: m}, cleanup, nil | ||
| 384 | } | 384 | } | ||
| 385 | 385 | ||||
| 386 | - | const officialZotfileCollection = "https://github.com/patriceckhart/agents" | |||
| 387 | - | ||||
| 388 | func resolveZotfileRef(ref string) (string, error) { | 386 | func resolveZotfileRef(ref string) (string, error) { | ||
| 389 | if u, err := url.Parse(ref); err == nil && (u.Scheme == "http" || u.Scheme == "https") { | 387 | if u, err := url.Parse(ref); err == nil && (u.Scheme == "http" || u.Scheme == "https") { | ||
| 390 | return ref, nil | 388 | return ref, nil | ||
| @@ -405,23 +403,23 @@ func resolveZotfileRef(ref string) (string, error) { | |||||
| 405 | if strings.HasSuffix(strings.ToLower(ref), ".zot") { | 403 | if strings.HasSuffix(strings.ToLower(ref), ".zot") { | ||
| 406 | return ref, nil | 404 | return ref, nil | ||
| 407 | } | 405 | } | ||
| 408 | - | collectionRef := strings.ReplaceAll(ref, "\\", "/") | 406 | + | githubRef := strings.ReplaceAll(ref, "\\", "/") |
| 409 | - | parts := strings.Split(collectionRef, "/") | 407 | + | parts := strings.Split(githubRef, "/") |
| 410 | - | if len(parts) == 1 && validZotfileCollectionSegment(parts[0]) { | 408 | + | if len(parts) == 2 && validGitHubZotfileSegment(parts[0]) && validGitHubZotfileSegment(parts[1]) { |
| 411 | - | return officialZotfileCollection + "/" + parts[0], nil | 409 | + | return "https://github.com/" + parts[0] + "/" + parts[1], nil |
| 412 | } | 410 | } | ||
| 413 | - | if len(parts) == 2 && validZotfileCollectionSegment(parts[0]) && validZotfileCollectionSegment(parts[1]) { | 411 | + | if len(parts) >= 3 { |
| 414 | - | // Preserve the original official-collection shorthand, where | 412 | + | for _, part := range parts { |
| 415 | - | // agents/<name> selects a subdirectory of patriceckhart/agents. | 413 | + | if !validGitHubZotfileSegment(part) { |
| 416 | - | if parts[0] == "agents" { | 414 | + | return ref, nil |
| 417 | - | return officialZotfileCollection + "/" + parts[1], nil | 415 | + | } |
| 418 | } | 416 | } | ||
| 419 | - | return "https://github.com/" + parts[0] + "/" + parts[1], nil | 417 | + | return "https://github.com/" + strings.Join(parts, "/"), nil |
| 420 | } | 418 | } | ||
| 421 | return ref, nil | 419 | return ref, nil | ||
| 422 | } | 420 | } | ||
| 423 | 421 | ||||
| 424 | - | func validZotfileCollectionSegment(s string) bool { | 422 | + | func validGitHubZotfileSegment(s string) bool { |
| 425 | return s != "" && s == strings.ToLower(s) && safeAgentName(s) == s | 423 | return s != "" && s == strings.ToLower(s) && safeAgentName(s) == s | ||
| 426 | } | 424 | } | ||
| 427 | 425 | ||||
M packages/agent/zotfile_test.go +10 -7
| @@ -162,7 +162,7 @@ func TestLoadZotfileRejectsUnsafeOrCollidingNames(t *testing.T) { | |||
| 162 | 162 | } | |
| 163 | 163 | } | |
| 164 | 164 | ||
| 165 | - | func TestResolveZotfileRefUsesLocalFirstThenOfficialCollection(t *testing.T) { | |
| 165 | + | func TestResolveZotfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { | |
| 166 | 166 | dir := t.TempDir() | |
| 167 | 167 | t.Chdir(dir) | |
| 168 | 168 | if err := os.Mkdir("local-agent", 0o700); err != nil { | |
| @@ -178,11 +178,13 @@ func TestResolveZotfileRefUsesLocalFirstThenOfficialCollection(t *testing.T) { | |||
| 178 | 178 | }{ | |
| 179 | 179 | {"local-agent", "local-agent"}, | |
| 180 | 180 | {"packed-agent", "packed-agent.zot"}, | |
| 181 | - | {"remote-agent", officialZotfileCollection + "/remote-agent"}, | |
| 181 | + | {"remote-agent", "remote-agent"}, | |
| 182 | 182 | {"missing.zot", "missing.zot"}, | |
| 183 | - | {"agents/remote-agent", officialZotfileCollection + "/remote-agent"}, | |
| 184 | - | {`agents\remote-agent`, officialZotfileCollection + "/remote-agent"}, | |
| 183 | + | {"agents/remote-agent", "https://github.com/agents/remote-agent"}, | |
| 184 | + | {`agents\remote-agent`, "https://github.com/agents/remote-agent"}, | |
| 185 | 185 | {"frkr/zot-archify", "https://github.com/frkr/zot-archify"}, | |
| 186 | + | {"acme/agents/reviewer", "https://github.com/acme/agents/reviewer"}, | |
| 187 | + | {`acme\agents\reviewers\go`, "https://github.com/acme/agents/reviewers/go"}, | |
| 186 | 188 | {"./missing-agent", "./missing-agent"}, | |
| 187 | 189 | {"Remote-Agent", "Remote-Agent"}, | |
| 188 | 190 | {"https://github.com/acme/agents/example", "https://github.com/acme/agents/example"}, | |
| @@ -259,8 +261,9 @@ func TestParseGitHubAgentURL(t *testing.T) { | |||
| 259 | 261 | input string | |
| 260 | 262 | owner, repo, ref, dir string | |
| 261 | 263 | }{ | |
| 262 | - | {"https://github.com/patriceckhart/agents/zot-maintenance", "patriceckhart", "agents", "HEAD", "zot-maintenance"}, | |
| 263 | - | {"https://github.com/patriceckhart/agents/tree/main/zot-maintenance", "patriceckhart", "agents", "main", "zot-maintenance"}, | |
| 264 | + | {"https://github.com/acme/agents/reviewer", "acme", "agents", "HEAD", "reviewer"}, | |
| 265 | + | {"https://github.com/acme/agents/tree/main/reviewer", "acme", "agents", "main", "reviewer"}, | |
| 266 | + | {"https://github.com/acme/agents/reviewers/go", "acme", "agents", "HEAD", "reviewers/go"}, | |
| 264 | 267 | {"https://github.com/acme/agent.git", "acme", "agent", "HEAD", ""}, | |
| 265 | 268 | } | |
| 266 | 269 | for _, tt := range tests { | |
| @@ -310,7 +313,7 @@ func TestLoadRemoteZotfileDownloadsTemporaryGitHubArchive(t *testing.T) { | |||
| 310 | 313 | githubArchiveURL = func(_, _, _ string) string { return server.URL } | |
| 311 | 314 | t.Cleanup(func() { githubArchiveURL = oldArchiveURL }) | |
| 312 | 315 | ||
| 313 | - | u, _ := url.Parse("https://github.com/patriceckhart/agents/zot-maintenance") | |
| 316 | + | u, _ := url.Parse("https://github.com/acme/agents/zot-maintenance") | |
| 314 | 317 | zf, cleanup, err := loadRemoteZotfile(u) | |
| 315 | 318 | if err != nil { | |
| 316 | 319 | t.Fatal(err) | |
| @@ -162,7 +162,7 @@ func TestLoadZotfileRejectsUnsafeOrCollidingNames(t *testing.T) { | |||||
| 162 | } | 162 | } | ||
| 163 | } | 163 | } | ||
| 164 | 164 | ||||
| 165 | - | func TestResolveZotfileRefUsesLocalFirstThenOfficialCollection(t *testing.T) { | 165 | + | func TestResolveZotfileRefUsesLocalFirstThenGitHubShorthand(t *testing.T) { |
| 166 | dir := t.TempDir() | 166 | dir := t.TempDir() | ||
| 167 | t.Chdir(dir) | 167 | t.Chdir(dir) | ||
| 168 | if err := os.Mkdir("local-agent", 0o700); err != nil { | 168 | if err := os.Mkdir("local-agent", 0o700); err != nil { | ||
| @@ -178,11 +178,13 @@ func TestResolveZotfileRefUsesLocalFirstThenOfficialCollection(t *testing.T) { | |||||
| 178 | }{ | 178 | }{ | ||
| 179 | {"local-agent", "local-agent"}, | 179 | {"local-agent", "local-agent"}, | ||
| 180 | {"packed-agent", "packed-agent.zot"}, | 180 | {"packed-agent", "packed-agent.zot"}, | ||
| 181 | - | {"remote-agent", officialZotfileCollection + "/remote-agent"}, | 181 | + | {"remote-agent", "remote-agent"}, |
| 182 | {"missing.zot", "missing.zot"}, | 182 | {"missing.zot", "missing.zot"}, | ||
| 183 | - | {"agents/remote-agent", officialZotfileCollection + "/remote-agent"}, | 183 | + | {"agents/remote-agent", "https://github.com/agents/remote-agent"}, |
| 184 | - | {`agents\remote-agent`, officialZotfileCollection + "/remote-agent"}, | 184 | + | {`agents\remote-agent`, "https://github.com/agents/remote-agent"}, |
| 185 | {"frkr/zot-archify", "https://github.com/frkr/zot-archify"}, | 185 | {"frkr/zot-archify", "https://github.com/frkr/zot-archify"}, | ||
| 186 | + | {"acme/agents/reviewer", "https://github.com/acme/agents/reviewer"}, | |||
| 187 | + | {`acme\agents\reviewers\go`, "https://github.com/acme/agents/reviewers/go"}, | |||
| 186 | {"./missing-agent", "./missing-agent"}, | 188 | {"./missing-agent", "./missing-agent"}, | ||
| 187 | {"Remote-Agent", "Remote-Agent"}, | 189 | {"Remote-Agent", "Remote-Agent"}, | ||
| 188 | {"https://github.com/acme/agents/example", "https://github.com/acme/agents/example"}, | 190 | {"https://github.com/acme/agents/example", "https://github.com/acme/agents/example"}, | ||
| @@ -259,8 +261,9 @@ func TestParseGitHubAgentURL(t *testing.T) { | |||||
| 259 | input string | 261 | input string | ||
| 260 | owner, repo, ref, dir string | 262 | owner, repo, ref, dir string | ||
| 261 | }{ | 263 | }{ | ||
| 262 | - | {"https://github.com/patriceckhart/agents/zot-maintenance", "patriceckhart", "agents", "HEAD", "zot-maintenance"}, | 264 | + | {"https://github.com/acme/agents/reviewer", "acme", "agents", "HEAD", "reviewer"}, |
| 263 | - | {"https://github.com/patriceckhart/agents/tree/main/zot-maintenance", "patriceckhart", "agents", "main", "zot-maintenance"}, | 265 | + | {"https://github.com/acme/agents/tree/main/reviewer", "acme", "agents", "main", "reviewer"}, |
| 266 | + | {"https://github.com/acme/agents/reviewers/go", "acme", "agents", "HEAD", "reviewers/go"}, | |||
| 264 | {"https://github.com/acme/agent.git", "acme", "agent", "HEAD", ""}, | 267 | {"https://github.com/acme/agent.git", "acme", "agent", "HEAD", ""}, | ||
| 265 | } | 268 | } | ||
| 266 | for _, tt := range tests { | 269 | for _, tt := range tests { | ||
| @@ -310,7 +313,7 @@ func TestLoadRemoteZotfileDownloadsTemporaryGitHubArchive(t *testing.T) { | |||||
| 310 | githubArchiveURL = func(_, _, _ string) string { return server.URL } | 313 | githubArchiveURL = func(_, _, _ string) string { return server.URL } | ||
| 311 | t.Cleanup(func() { githubArchiveURL = oldArchiveURL }) | 314 | t.Cleanup(func() { githubArchiveURL = oldArchiveURL }) | ||
| 312 | 315 | ||||
| 313 | - | u, _ := url.Parse("https://github.com/patriceckhart/agents/zot-maintenance") | 316 | + | u, _ := url.Parse("https://github.com/acme/agents/zot-maintenance") |
| 314 | zf, cleanup, err := loadRemoteZotfile(u) | 317 | zf, cleanup, err := loadRemoteZotfile(u) | ||
| 315 | if err != nil { | 318 | if err != nil { | ||
| 316 | t.Fatal(err) | 319 | t.Fatal(err) | ||