From 09f6a555b4ba36fe8c86b0500a4c86156dc1adc3 Mon Sep 17 00:00:00 2001 From: yanivt-jfrog <209457562+yanivt-jfrog@users.noreply.github.com> Date: Thu, 11 Jun 2026 14:29:28 +0000 Subject: [PATCH] chore: sync skills to v0.5.0 --- marketplace.json | 19 +- .../SKILL.md | 286 +++++++ plugin/skills/jfrog/.gitignore | 1 + plugin/skills/jfrog/SKILL.md | 500 ++++++++++++ plugin/skills/jfrog/assets/.gitkeep | 0 .../jfrog/references/apptrust-entities.md | 154 ++++ .../jfrog/references/artifactory-api-gaps.md | 206 +++++ .../references/artifactory-aql-syntax.md | 656 +++++++++++++++ .../jfrog/references/artifactory-entities.md | 236 ++++++ .../references/artifactory-operations.md | 172 ++++ .../jfrog/references/catalog-entities.md | 219 +++++ ...eral-bulk-operations-and-agent-patterns.md | 103 +++ .../references/general-parallel-execution.md | 133 ++++ .../references/general-use-case-hints.md | 28 + .../references/jfrog-brand-html-report.md | 98 +++ .../references/jfrog-cli-install-upgrade.md | 30 + .../jfrog/references/jfrog-entity-index.md | 112 +++ .../jfrog/references/jfrog-login-flow.md | 126 +++ .../jfrog/references/jfrog-url-references.md | 51 ++ .../references/onemodel-common-patterns.md | 323 ++++++++ .../jfrog/references/onemodel-graphql.md | 446 +++++++++++ .../references/onemodel-query-examples.md | 753 ++++++++++++++++++ .../references/platform-access-entities.md | 200 +++++ .../references/platform-admin-api-gaps.md | 164 ++++ .../references/platform-admin-operations.md | 58 ++ .../skills/jfrog/references/projects-api.md | 243 ++++++ .../references/release-lifecycle-entities.md | 180 +++++ .../references/stored-packages-entities.md | 165 ++++ .../skills/jfrog/references/xray-entities.md | 740 +++++++++++++++++ .../skills/jfrog/scripts/check-environment.sh | 178 +++++ .../scripts/jfrog-login-register-session.sh | 84 ++ .../scripts/jfrog-login-save-credentials.sh | 120 +++ 32 files changed, 6781 insertions(+), 3 deletions(-) create mode 100644 plugin/skills/jfrog-package-safety-and-download/SKILL.md create mode 100644 plugin/skills/jfrog/.gitignore create mode 100644 plugin/skills/jfrog/SKILL.md create mode 100644 plugin/skills/jfrog/assets/.gitkeep create mode 100644 plugin/skills/jfrog/references/apptrust-entities.md create mode 100644 plugin/skills/jfrog/references/artifactory-api-gaps.md create mode 100644 plugin/skills/jfrog/references/artifactory-aql-syntax.md create mode 100644 plugin/skills/jfrog/references/artifactory-entities.md create mode 100644 plugin/skills/jfrog/references/artifactory-operations.md create mode 100644 plugin/skills/jfrog/references/catalog-entities.md create mode 100644 plugin/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md create mode 100644 plugin/skills/jfrog/references/general-parallel-execution.md create mode 100644 plugin/skills/jfrog/references/general-use-case-hints.md create mode 100644 plugin/skills/jfrog/references/jfrog-brand-html-report.md create mode 100644 plugin/skills/jfrog/references/jfrog-cli-install-upgrade.md create mode 100644 plugin/skills/jfrog/references/jfrog-entity-index.md create mode 100644 plugin/skills/jfrog/references/jfrog-login-flow.md create mode 100644 plugin/skills/jfrog/references/jfrog-url-references.md create mode 100644 plugin/skills/jfrog/references/onemodel-common-patterns.md create mode 100644 plugin/skills/jfrog/references/onemodel-graphql.md create mode 100644 plugin/skills/jfrog/references/onemodel-query-examples.md create mode 100644 plugin/skills/jfrog/references/platform-access-entities.md create mode 100644 plugin/skills/jfrog/references/platform-admin-api-gaps.md create mode 100644 plugin/skills/jfrog/references/platform-admin-operations.md create mode 100644 plugin/skills/jfrog/references/projects-api.md create mode 100644 plugin/skills/jfrog/references/release-lifecycle-entities.md create mode 100644 plugin/skills/jfrog/references/stored-packages-entities.md create mode 100644 plugin/skills/jfrog/references/xray-entities.md create mode 100755 plugin/skills/jfrog/scripts/check-environment.sh create mode 100755 plugin/skills/jfrog/scripts/jfrog-login-register-session.sh create mode 100755 plugin/skills/jfrog/scripts/jfrog-login-save-credentials.sh diff --git a/marketplace.json b/marketplace.json index 9c8506a..815e098 100644 --- a/marketplace.json +++ b/marketplace.json @@ -9,10 +9,23 @@ { "name": "jfrog", "description": "JFrog Platform integration with MCP, security skills, and supply-chain best practices", - "version": "1.0.3", + "version": "1.0.4", "source": "plugin", - "categories": ["security", "artifact-management", "supply-chain", "devops", "mcp", "mlops", "agent-guard", "ai-catalog"], - "platforms": ["darwin", "linux", "windows"], + "categories": [ + "security", + "artifact-management", + "supply-chain", + "devops", + "mcp", + "mlops", + "agent-guard", + "ai-catalog" + ], + "platforms": [ + "darwin", + "linux", + "windows" + ], "requiredPermissions": [ "network", "shell" diff --git a/plugin/skills/jfrog-package-safety-and-download/SKILL.md b/plugin/skills/jfrog-package-safety-and-download/SKILL.md new file mode 100644 index 0000000..e510898 --- /dev/null +++ b/plugin/skills/jfrog-package-safety-and-download/SKILL.md @@ -0,0 +1,286 @@ +--- +name: jfrog-package-safety-and-download +description: >- + Check JFrog Public Catalog and stored packages for a version, interpret + catalog security signals, and download through Artifactory (Jfrog Platform + locations, remote cache, curation-aware package managers, or repo proxy). + Use when the user asks whether a package is safe, allowed, curated, or + wants to download npm, Maven, PyPI, Go, or similar packages via JFrog. + Do NOT use for pure CVE or vulnerability lookups (e.g. "details on + CVE-2021-23337") — those are handled by the jfrog skill's Public security + domain queries without this workflow. +metadata: + role: workflow +--- + +# JFrog Package Safety and Download + +## Prerequisites + +- Read `../jfrog/SKILL.md` for JFrog Platform concepts, domain model, CLI setup, and API patterns. +- **OneModel shapes drift by server version.** Before inventing GraphQL fields or `where` filters, read `../jfrog/references/onemodel-graphql.md` (schema fetch workflow) and `../jfrog/references/onemodel-query-examples.md` (**Public packages**, **Stored packages**). Regenerate or verify queries against `GET "$JFROG_URL/onemodel/api/v1/supergraph/schema"` when examples fail validation. + +## Workflow + +# Package safety check and download workflow + +When to read this file: + +- User asks to **check if a package is safe** and/or **download** it. +- User asks to **download a package** from Artifactory. +- User mentions checking a package for **curation** approval. +- User wants to know if a package is **allowed** or **approved** for use. + +## Workflow overview + +```mermaid +flowchart TD + A[User requests package check / download] --> B{Package in Public Catalog?} + B -->|Yes| C[Get latest version from Catalog] + B -->|No| D{Package in Jfrog Platform Stored Packages?} + D -->|Yes| E[Get latest version from Stored Packages] + D -->|No| F[Package not found — stop] + C --> G{Latest version in Jfrog Platform?} + E --> G + G -->|Yes| H[Safe — download from Jfrog Platform] + G -->|No| I{Curation entitled?} + I -->|Yes| J[Check curation policy via API] + I -->|No| K[Download via remote repo] + J -->|200 Allowed| K + J -->|403 Blocked| M[Report curation blocked — stop] +``` + +### Parallelization opportunities + +Several steps in this workflow are independent and can run in parallel to +reduce total latency: + +- **Step 1 + Step 1 fallback**: When package type is known, query both the + Public Catalog (`getPackage`) and Stored Packages (`getPackage`) in + parallel. Use whichever returns data; if the Public Catalog returns a hit, + prefer its `latestVersion` for Step 2. +- **Step 3 + Step 5**: After determining the version, query stored package + versions (Jfrog Platform check) and curation entitlement + (`/api/system/version`) in parallel. Both are independent reads — the + curation result is needed immediately if the Jfrog Platform check returns + empty. + +When issuing parallel Shell calls, each `jf api` call authenticates +independently against the active `jf config` server; no shell state needs +to be passed between calls. + +## Step 1: Find the package + +Search the **Public Catalog** first via OneModel GraphQL, then fall back to +**Stored Packages** if not found. + +Execute the query through `jf api` as described in +`../jfrog/references/onemodel-graphql.md`; refer to +`../jfrog/references/onemodel-query-examples.md` for concrete query shapes. + +**When package type is known** (e.g. `npm`, `maven`, `pypi`), use +`publicPackages.getPackage(type:, name:)` (see *Get a public package*). +Include the `latestVersion { version }` selection set — `latestVersion` is +an object, not a scalar. + +**When type is unknown**, use `publicPackages.searchPackages` with +`nameContains` (see *Search public packages*). Add `type:` when the user +narrows the ecosystem. + +- **Found** → note `type` and `latestVersion.version`. Proceed to Step 2. +- **Not found** → the package may be 1st/2nd party. Search **Stored Packages** + using `storedPackages.searchPackages` or `storedPackages.getPackage` (see + *Stored packages domain* in `onemodel-query-examples.md`). Prefer + filtering by `type` when known; if not, use `nameContains` alone. + - **Found** → note `type` and `latestVersionName` (or derive a version from + `versionsConnection`). Proceed to Step 2. + - **Not found in either** → report "package not found" and stop. + +If multiple results with different `type` values, ask the user which package +type they mean. + +## Step 2: Determine latest version + +| Source | Version field | +|--------|--------------| +| Public Catalog | `latestVersion.version` (object selection required) | +| Jfrog Platform Stored Packages | `latestVersionName` on `StoredPackage`, or highest entry from `versionsConnection` | + +## Step 3: Check if package + latest version exists in Jfrog Platform + +Query stored package versions using `storedPackages.searchPackageVersions` +with a `hasPackageWith` filter (see `../jfrog/references/onemodel-query-examples.md` +→ *Search stored package versions*). Add a `version` filter for the specific +version from Step 2, and request `locationsConnection` to get repository +details (`repositoryKey`, `repositoryType`, `leadArtifactPath`). + +Execute the query through `jf api` (see +`../jfrog/references/onemodel-graphql.md` for the invocation pattern). + +- **Found with locations** → package is in the Jfrog Platform. Report as **safe to + download**. Proceed to Step 4. +- **Not found** → proceed to Step 5. + +## Step 4: Download from Jfrog Platform + +Use the location info from Step 3. Binary artifact downloads go through +`jf rt dl` — **not** `jf api`. `jf api` is the unified entry point for the +JFrog REST APIs (metadata, admin, curation, etc.) and does not expose the +`-L` / `-o` flags needed to stream binary content through a redirect chain. + +**`` must be a full file path** (e.g. +`./downloads/lodash-4.18.1.tgz`), not a bare directory. `jf rt dl --flat` +treats the target as a file name; passing a directory causes a misleading +"open path: is a directory" error. + +| `repositoryType` | Strategy | +|-------------------|----------| +| `local` or `federated` | `jf rt dl "/" --flat` | +| `remote` | `jf rt dl` against the **base** remote repo (strip any trailing `-cache`) — it transparently triggers the remote fetch when the artifact is not yet cached | + +**local / federated / remote download:** + +```bash +jf rt dl "/" --flat +``` + +**Resolving the remote repo key:** The `repositoryKey` returned by OneModel +for remote locations often already ends in `-cache` (e.g. +`devNPM-remote-cache`). `jf rt dl` needs the **base remote repo name** +(without `-cache`). Strip the `-cache` suffix when present (e.g. +`devNPM-remote-cache` → `devNPM-remote`). If the key does not end in +`-cache`, use it as-is. + +See the **Protocol endpoints** table below for the package-type-specific +path format inside the repo. + +## Step 5: Check curation entitlement + +```bash +jf api /artifactory/api/system/version \ + | jq '.addons | index("curation") != null' +``` + +- `true` → curation is entitled. Proceed to Step 6a. +- `false` → curation not available. Proceed to Step 6b. + +## Step 6a: Check curation policy and download + +When curation is entitled, use the Xray curation API to check whether the +package version is allowed across all repositories before downloading. + +```bash +RESPONSE_FILE="/tmp/curation-status-$$.json" +PAYLOAD_FILE="/tmp/curation-payload-$$.json" +STDERR_FILE="/tmp/curation-err-$$.log" + +jq -n \ + --arg type "" \ + --arg name "" \ + --arg version "" \ + '{packageType:$type, packageName:$name, packageVersion:$version}' \ + > "$PAYLOAD_FILE" + +set +e +jf api /xray/api/v1/curation/package_status/all_repos \ + -X POST -H "Content-Type: application/json" \ + --input "$PAYLOAD_FILE" \ + > "$RESPONSE_FILE" 2> "$STDERR_FILE" +RC=$? +set -e +echo "RC=$RC"; echo "$RESPONSE_FILE" +``` + +Supported `packageType` values: `npm`, `pypi`, `maven`, `go`, `nuget`, +`docker`, `gradle`. + +**Interpreting the result with `jf api`**: unlike plain `curl`, `jf api` +surfaces the HTTP result through its **exit code** and a +`" [Warn] ... returned 4xx/5xx"` line on **stderr** (not a +`%{http_code}` suffix in stdout). The response body is always written to +stdout. Parse both: + +```bash +if [ "$RC" -eq 0 ]; then + echo "Package is allowed by curation." +elif grep -q 'returned 403' "$STDERR_FILE"; then + echo "Blocked by curation policy:" + cat "$RESPONSE_FILE" +else + echo "Curation check failed (rc=$RC):" + cat "$STDERR_FILE" +fi +``` + +**Evaluate the outcome:** + +- **exit 0** → package is **allowed** by curation policy. Proceed to + download via a remote repo (same as Step 6b). +- **`returned 403` on stderr** → package is **blocked** by a curation + policy. The response body explains which policy rule blocked it. Report + the block reason to the user and stop — do not attempt to download. +- **Any other non-zero exit** → treat as an operational failure (auth, DNS, + endpoint disabled) and report. + +## Step 6b: Download without curation + +When curation is not entitled and the package is not in the Jfrog Platform, +download directly through a remote repo. + +1. **Find a remote repo** of the right package type: + + ```bash + jf api \ + "/artifactory/api/repositories?type=remote&packageType=" \ + | jq '.[].key' + ``` + +2. **Download** — use `jf rt dl` against the base remote repo (without + `-cache`); it handles both cached and uncached artifacts: + + ```bash + jf rt dl "/" --flat + ``` + +## Artifact paths by package type + +Use these path patterns when `leadArtifactPath` is not available from +OneModel. The leading `/` is the base repo key you pass to `jf rt dl`. + +| Type | `jf rt dl` target pattern | +|--------|-------------------------------------------------------------------------| +| `npm` | `//-/-.tgz` | +| `pypi` | `///-.tar.gz` | +| `maven`| `////-.jar` | +| `go` | `//@v/.zip` | + +## Gotchas + +- **Binary downloads vs. `jf api`**: `jf api` is for REST APIs, not binary + content. It does not follow redirects transparently into a binary payload + and does not expose `-L` / `-o`. Always use `jf rt dl` (against the base + remote repo, not the `-cache` one) for the actual artifact download. +- **`jf rt dl` and uncached remotes**: `jf rt dl "/"` — + targeting the **base** remote repo rather than `-cache/` — + transparently triggers the remote fetch and caches the artifact. Do not + try to pre-query the proxy via `jf api`. +- **`jf rt dl --flat` target must be a file path**: When downloading a + single artifact, pass a full output **file** path (e.g. + `./downloads/lodash-4.18.1.tgz`), not a directory. The CLI opens the target + path as a file; a directory causes a cryptic "open path: is a directory" + error that retries four times before failing. Derive the filename from + `leadArtifactPath` (take the segment after the last `/`). +- **Package type detection**: If the user doesn't specify the package type, + the Public Catalog search by name alone may return multiple types. Ask the + user to disambiguate before proceeding. +- **Curation endpoint lives under Xray**: use + `/xray/api/v1/curation/package_status/all_repos` (via `jf api`). Do not + prefix it with `/artifactory`. +- **Curation result discrimination with `jf api`**: the 200/403 signal comes + from `jf api`'s **exit code** plus a `returned NNN` line on **stderr**, + not from a `%{http_code}` appended to stdout. Capture stderr to a file + (`2> "$STDERR_FILE"`) and branch on `RC` + `grep 'returned 403'` as shown + in Step 6a. +- **Curation API package type values**: Must be lowercase and match one of + `npm`, `pypi`, `maven`, `go`, `nuget`, `docker`, `gradle`. Other values + will return an error. diff --git a/plugin/skills/jfrog/.gitignore b/plugin/skills/jfrog/.gitignore new file mode 100644 index 0000000..3215b96 --- /dev/null +++ b/plugin/skills/jfrog/.gitignore @@ -0,0 +1 @@ +local-cache/ diff --git a/plugin/skills/jfrog/SKILL.md b/plugin/skills/jfrog/SKILL.md new file mode 100644 index 0000000..6b95e18 --- /dev/null +++ b/plugin/skills/jfrog/SKILL.md @@ -0,0 +1,500 @@ +--- +name: jfrog +version: "0.5.0" +description: >- + Interact with the JFrog Platform via the JFrog CLI and REST/GraphQL APIs. + Use this skill when the user wants to manage Artifactory repositories, + upload or download artifacts, manage builds, configure permissions, + manage users and groups, work with access tokens, configure JFrog CLI + servers, search artifacts, manage properties, set up replication, + manage JFrog Projects, run security audits or scans, look up CVE details, + query exposures scan results from JFrog Advanced Security, manage + release bundles and lifecycle operations, aggregate or export platform + data, or perform any JFrog Platform administration task. + Also use when the user mentions jf, jfrog, artifactory, xray, distribution, + evidence, apptrust, onemodel, graphql, workers, mission control, curation, + advanced security, exposures, or any JFrog product name. +compatibility: >- + Requires jq on PATH. +metadata: + role: base +--- + +# JFrog Skill + +The foundational skill for all JFrog agent interactions. Covers JFrog Platform concepts, `jf` CLI setup and authentication, and intent routing to workflow skills. + +Interact with the JFrog Platform through the JFrog CLI (`jf`) and, where the +CLI falls short, through REST APIs and GraphQL. In code examples below, +`` refers to this skill's directory and is resolved automatically +by the agent. If the agent does not resolve it, determine the path by locating +this SKILL.md file and using its parent directory. + +## Prerequisites + +The following tools must be available on `PATH`: + +| Tool | Purpose | +|------|---------| +| `jq` | JSON parsing of CLI and API output | + +All HTTP traffic to JFrog Platform APIs goes through the `jf` CLI itself +(`jf api`, see [Invoking platform APIs with `jf api`](#invoking-platform-apis-with-jf-api) below) — +no standalone `curl` is required for any JFrog interaction. + +## Environment check + +Before your first JFrog operation in a session, run the environment check. +It verifies the CLI is installed, checks for updates, and exports +`JFROG_CLI_USER_AGENT` so every outbound request is identifiable: + +```bash +eval "$(JFROG_SKILL_MODEL="" bash /scripts/check-environment.sh)" +``` + +Set `JFROG_SKILL_MODEL` to the precise slug of the underlying LLM, with +version (e.g. `opus-4.7`, `sonnet-4.5`, `gpt-5-codex`, `gemini-2.5-pro`). +**Do not** use harness/role names like `subagent`, `cursor-agent`, `agent`, +`assistant`, or a family without a version (`claude`, `gpt`). Subagents pass +through the parent's slug. If genuinely unknown, use `unknown`. + +The `eval` is required — the script outputs +`export JFROG_CLI_USER_AGENT='model/ jfrog-skills/ jfrog-cli-go/'` +on stdout. The JFrog CLI picks this up natively and injects it as the +`User-Agent` header on every HTTP request. JSON state is printed to stderr +for informational purposes (also written to the cache file). + +The script uses a 24-hour cache at `/local-cache/jfrog-skill-state.json`. If the +cache is fresh, it returns immediately. If stale or missing, it checks whether +`jf` is installed, its version, and whether a newer version is available. + +- Exit 0: cache is fresh, CLI is ready — proceed +- Exit 1: cache was stale and has been refreshed, CLI is ready — proceed +- Exit 2: `jf` is not installed — **STOP** (see below) +- Exit 3: `jf` is installed but below the minimum version required by this skill (the script prints the minimum and the detected version to stderr) — **STOP** (see below) + +Bypass the cache only when the user explicitly asks to install, upgrade, or +reconfigure the CLI. + +**On exit 2 or 3, stop and ask the user to install or upgrade.** Do not work +around it with `jf rt curl`, raw `curl`, or other fallbacks — see +`references/jfrog-cli-install-upgrade.md`. + +### JSON parsing (`jq`) + +Use **`jq`** for all JSON parsing of CLI and API output (pipes, `-r`, filters). + +## Network permissions + +JFrog servers are not on the default sandbox network allowlist. Every Shell +call that contacts a JFrog server requires +`required_permissions: ["full_network"]`. + +Without this permission, commands fail silently: `jf` exits with code 1 and +empty output, and downstream JSON parsing crashes. All JFrog operations that +touch the network need this permission. + +### Agent execution environments + +`check-environment.sh` does **not** call your JFrog server, but it may make an +outbound request to `releases.jfrog.io` for version checking and may **write** +`/local-cache/jfrog-skill-state.json` when the cache is stale. In a **sandboxed** +agent environment, **`full_network` alone may not suffice**: if the workspace +cannot be written, the check can fail before any JFrog call. Request +permissions that allow writing `/local-cache` (or run +outside a restrictive sandbox) when you see filesystem errors from the +environment check. + +### `local-cache/` — allowed files only + +`/local-cache/` is **not** a general scratch or temp directory. Use +it **only** for these two artifacts: + +1. **`jfrog-skill-state.json`** — written by `scripts/check-environment.sh` + (24-hour CLI check cache). +2. **`onemodel-schema-${JFROG_SERVER_ID}.graphql`** — cached OneModel supergraph + schema (see `references/onemodel-graphql.md`). + +**Do not** save HTTP response bodies, GraphQL query results, ad-hoc JSON, reports, +or any other temporary files under `local-cache/`. Write those to a host temp +path instead (for example `/tmp/-$$.json` or `mktemp -d`), echo the path +when a follow-up Shell step must read the file — same pattern as *Preserving +command output* below. + +Apply `full_network` on the **first** Shell call that hits JFrog. Once +granted for a session, the agent environment typically retains it for +subsequent calls, but always include it explicitly to avoid silent failures. + +## Server management + +Server configuration is always read live from `jf config` (never cached). + +- **List servers**: `jf config show` (local operation, no network needed) +- **Use a specific server**: pass `--server-id ` to any command +- **Switch default**: `jf config use ` +- **Add a new server**: read `references/jfrog-login-flow.md` for the full + login procedure (web login or manual token setup) + +### Server selection rules (mandatory) + +Exactly one server (or an explicit set of servers) must be resolved before any +operation. The rules are strict and apply to every CLI command, API call, and +subagent prompt: + +1. **User named specific server(s)** — use those and only those. Pass + `--server-id ` to every `jf` command. Do not touch any other + configured server. +2. **User did not name a server** — use the current default server and only + it. Determine the default via `jf config show` (the entry marked as + default). If no default is set, stop and ask the user which server to use. +3. **Verify before executing** — after resolving the server, confirm it + exists in `jf config show` output before running any command against it. + If the server-id is not listed, stop and tell the user. + +Do not fall back to a different server. Silently switching servers is +dangerous because different servers hold different data, permissions, and +configurations — an operation that succeeds on the wrong server can corrupt +state, leak data across environments, or produce results the user cannot +reproduce. If the resolved server produces any error — does not exist in +`jf config`, authentication failure (401/403), network error, connection +refused, or any other failure — stop immediately and report the error to the +user. Do not try other configured servers, do not iterate through the server +list, and do not silently switch servers. Ask the user how to proceed. + +## Command discovery + +Use the commands listed below as your primary reference. Run `--help` to +verify options you are unsure about or to discover commands not listed here — +do not rely on memorized commands outside this skill, as they may be outdated. + +1. `jf --help` — list all namespaces and top-level commands +2. `jf --help` — list subcommands in a namespace +3. `jf --help` — show usage, arguments, and options + +### CLI namespaces + +| Namespace | Alias | Product | +|-----------|-------|---------| +| `rt` | | Artifactory | +| `xr` | | Xray | +| `ds` | | Distribution V1 | +| `at` | `apptrust` | AppTrust | +| `evd` | | Evidence | +| `mc` | | Mission Control | +| `worker` | | Workers | +| `config` | `c` | CLI server configuration | +| `plugin` | | CLI plugin management | +| `ide` | | IDE integration | + +> **Sunset notice:** JFrog Pipelines has been sunset and is no longer supported. +> Do not use the `pl` CLI namespace or the Pipelines REST API +> (`/pipelines/api/...`). If a user asks about Pipelines, inform them the +> product has been sunset. + +Top-level lifecycle commands (no namespace): `rbc`, `rbp`, `rbd`, `rba`, +`rbf`, `rbe`, `rbi`, `rbs`, `rbu`, `rbdell`, `rbdelr`. + +Top-level security commands: `audit`, `scan`, `build-scan`, `curation-audit`, +`sbom-enrich`. + +Top-level other: `access-token-create` (`atc`), `login`, `how`, `stats`, +`generate-summary-markdown`, `exchange-oidc-token`, `completion`. + +## Artifactory operations + +Artifactory resources are managed through the `jf rt` namespace — repos, files, +builds, permissions, users/groups, and replication. Read +`references/artifactory-operations.md` when performing any of these operations. + +## Platform administration + +Access tokens, login, stats, projects, and system health. Read +`references/platform-admin-operations.md` when performing any of these +operations. + +## Invoking platform APIs with `jf api` + +When the CLI lacks a dedicated subcommand, use `jf api` — the unified entry +point for every JFrog Platform REST and GraphQL endpoint, auto-authenticated +against the resolved server. **Do not use `jf rt curl` or `jf xr curl`** — +they are superseded by `jf api`. All `jf api` calls require +`required_permissions: ["full_network"]` (see [Network permissions](#network-permissions)). + +### Product-prefix table + +`jf api` requires the **full** path including the product prefix; omitting it +returns 404. + +| Product | Path prefix | +|---------|-------------| +| Artifactory | `/artifactory/api/...` | +| Xray | `/xray/api/...` | +| Access (users, groups, tokens, permissions, projects) | `/access/api/...` | +| Evidence | `/evidence/api/...` | +| Release Lifecycle | `/lifecycle/api/...` | +| AppTrust | `/apptrust/api/...` | +| Distribution | `/distribution/api/...` | +| OneModel (GraphQL) | `/onemodel/api/v1/graphql`, `/onemodel/api/v1/supergraph/schema` | +| Mission Control | `/mc/api/...` | +| Curation | `/xray/api/v1/curation/...` (lives under Xray) | + +### Examples + +```bash +jf api /artifactory/api/repositories +jf api /artifactory/api/system/version --server-id + +# AQL (POST with text/plain body) +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" -d '' +``` + +Common flags: `-X/--method`, `-H/--header`, `-d/--data`, `--input `, +`--server-id`, `--timeout`. Body on stdout, status on stderr — see +[Gotchas](#gotchas). + +### GraphQL (OneModel) + +OneModel is the unified GraphQL API. **Do not** embed the query inside a JSON +literal (`-d '{"query":"..."}'`) — escaping breaks requests. Build the payload +with `jq -n --arg`, pass it via `--input`, and save the response to a file +before running `jq` on it. + +```bash +QUERY='{ evidence { searchEvidence(first: 5, where: { hasSubjectWith: { repositoryKey: "my-repo-local" } }) { totalCount } } }' +PAYLOAD=/tmp/onemodel-payload-$$.json RESPONSE=/tmp/onemodel-$$.json +jq -n --arg q "$QUERY" '{query:$q}' > "$PAYLOAD" +jf api /onemodel/api/v1/graphql -X POST \ + -H "Content-Type: application/json" --input "$PAYLOAD" > "$RESPONSE" +jq . "$RESPONSE" +``` + +Schema discovery: `jf api /onemodel/api/v1/supergraph/schema > "$SCHEMA_FILE"` +(store only under `/local-cache/`, never query responses). Read +`references/onemodel-graphql.md` for the full workflow (schema fetch, +validation, pagination, errors), plus `references/onemodel-query-examples.md` +and `references/onemodel-common-patterns.md` for query shapes, pagination, +variables, and dates. + +## Structured inputs + +Several CLI commands require JSON template files. The templates are normally +created by interactive wizard commands (`jf rt rpt`, `jf rt ptt`, `jf rt rplt`) +which agents cannot use. Instead, retrieve an existing config via REST API as a +starting point and modify it: + +```bash +jf api /artifactory/api/repositories/ +``` + +For other Artifactory or platform REST patterns, or when you need more than +this repo GET, see **Any API gap** under [When to read reference files](#when-to-read-reference-files). + +## Gotchas + +- JFrog network calls require `required_permissions: ["full_network"]` in the + Shell tool. Without it, commands fail silently with empty output. The + environment check does **not** call your JFrog server (it may contact + `releases.jfrog.io` for version checking), but it may need **workspace + write** access for its cache file (see [Agent execution environments](#agent-execution-environments)). +- `jf api` requires the **product prefix** in the path (`/artifactory/...`, `/xray/...`, `/access/...`, `/evidence/...`, + `/lifecycle/...`, `/apptrust/...`, `/distribution/...`, `/onemodel/...`, + `/mc/...`). Omitting the prefix returns 404. See the + [product-prefix table](#product-prefix-table) above. +- `jf api` writes the body (success or error JSON) to **stdout** and + `[Info] Http Status: NNN` to **stderr** on every call; non-2xx also exits + 1 and adds `[Warn] jf api: returned NNN`. Pipe stdout to + `jq` directly; **never `2>&1 | jq`** — stderr corrupts the JSON. To keep + diagnostics: `jf api 2>/tmp/err-$$.log | jq .`. +- `jf api` has **no `-L`** (follow redirects) and **no `-o`** (output file). + Save bodies with shell redirection + (`jf api ... > /tmp/out-$$.json`); for + binary downloads through the Artifactory remote proxy prefer `jf rt dl`, + which handles the cache and redirect semantics natively. +- Remote repository content is stored in a `-cache` suffixed repo. Properties + and AQL queries for remote repo artifacts must target the cache repo. + Conversely, `/api/repositories/` only accepts the parent remote key + (without `-cache`) — strip the suffix for configuration lookups. +- **Do not use `jf rt search`** — always use a direct AQL query via + `jf api /artifactory/api/search/aql -X POST -H "Content-Type: text/plain" -d ''`. + See `references/artifactory-aql-syntax.md`. +- Use `--quiet` flag for non-interactive execution (suppresses confirmation + prompts). **Caution:** `--quiet` is not a global flag — commands that do not + support it (e.g. `jf rt s`, `jf rt ping`) will fail with misleading errors + like "Wrong number of arguments" or "flag provided but not defined". Check + `--help` for a command before adding `--quiet`. +- Use `--server-id` when targeting a non-default server. If a command fails + with `--server-id`, do not retry without it — that silently targets the + default server instead. See [Server selection rules](#server-selection-rules-mandatory). +- Never use interactive commands. All JFrog CLI operations must be performed + non-interactively. Known interactive commands to avoid: `jf config add`, + `jf login`, `jf rt repo-template`, `jf rt permission-target-template`, and + `jf rt replication-template`. For server setup, follow `references/jfrog-login-flow.md`. + For templates, use JSON schemas or REST API. If a command prompts for input + unexpectedly, find the non-interactive alternative via `--help` or REST API. +- `jf config export` output is base64-encoded JSON. Decode with + `base64 -d | jq` to extract fields. +- Build info lookups require a scope (`?buildRepo=` or `?project=`) — + resolve it before calling the API. See `references/artifactory-operations.md` + §Retrieving build info for the full workflow. +- If a `jf api` call returns 401, the configured token may have expired or + been rotated — ask the user to re-run the login flow (see + `references/jfrog-login-flow.md`) for the **same** server. If 403, the + token lacks required permissions. If 404, verify the endpoint path + (especially the product prefix) and target server version. On any of + these errors, do not try a different configured server as a workaround — + that targets a different environment. Report the error and ask the user. +- **Xray contextual analysis:** the summary artifact response has two + applicability fields — `applicability` (top-level, often null) and + `applicability_details` (always present with a `result` string). **Use + `applicability_details[].result` for counts and summaries.** Using the + top-level `applicability` field for aggregation produces wrong counts because + it is null when no scanner exists. See `references/xray-entities.md` + §Contextual analysis for the eight possible result values and jq snippets. +- **OneModel GraphQL:** always fetch the supergraph schema from the **same** + server you query before building operations (schemas differ by deployment); + cache, validate, and execute per `references/onemodel-graphql.md`. +- Never duplicate a network-fetching command to retry `jq` parsing — save the + response to a temp file first (see [Preserving command output](#preserving-command-output)). +- When collecting detail responses in a loop (e.g. per-repo GETs), validate + each body with `jq -e .` before appending to a results file. One non-JSON + or empty response corrupts a downstream `jq -s` slurp. Write validated + lines to an NDJSON file, then `jq -s '.' file.ndjson` to produce the final + array. See `references/general-bulk-operations-and-agent-patterns.md`. +- Accumulated edge cases from real tasks live in `references/general-use-case-hints.md` + — read when debugging odd failures; **append** a short entry when you confirm + a new, reusable gotcha. + +## Cautious execution + +Do not run commands speculatively. Before executing any JFrog CLI command or +API call: + +1. Confirm the operation is needed to fulfill the user's request +2. Resolve the target server using the **Server selection rules** above — + there must be no ambiguity about which server is used +3. For mutating operations (create, update, delete, upload), confirm with the + user unless the intent is clearly implied +4. Prefer read operations first to understand current state before making changes +5. If any command fails with a server-level error (not found, auth, network), + stop and ask the user — never retry against a different server +6. **Never invent preparatory mutations.** If the requested operation fails + because a precondition is not met (artifact missing from the specified repo, + repository does not exist, package not at the expected location, build not + found), **stop and report the gap to the user**. Do not perform copy, move, + upload, create-repo, or any other mutating operation to satisfy the + precondition unless the user explicitly asks for it. These "helper" mutations + can have cascading effects the user has not considered — virtual repository + resolution changes, storage quota consumption, replication triggers, Xray + re-indexing, or permission propagation. + +## Batch and parallel execution + +When a task requires multiple independent operations, use the lightest +parallelism mechanism that fits. Three tiers: (1) batch commands in a single +Shell call using loops or `&`, (2) issue parallel Shell tool calls, (3) launch +parallel subagents for large fan-out. Read `references/general-parallel-execution.md` +(~135 lines) for tier selection, examples, and subagent prompt structuring. + +## Preserving command output + +When a CLI command or API call returns data, redirect the output to a temporary +file so you can re-read it without re-executing the call: + +```bash +OUT=/tmp/jf-repos-$$.json +jf api /artifactory/api/repositories > "$OUT" +echo "$OUT" +``` + +Use `$$` (the shell PID) in the filename to prevent collisions across +concurrent sessions or processes. + +**Cross-call gotcha:** each Shell tool invocation runs in a new process with a +different PID, so `$$` expands to a different value in each call. Always +**echo the expanded filename** so the agent can read it from the output and +reuse the literal path in subsequent calls. Three patterns, in priority order: + +1. **`$$` + echo** (preferred): use `$$` for collision safety, echo the path + as shown above. The agent reads `/tmp/jf-repos-12345.json` from the output + and passes that literal value to the next Shell call. +2. **Session ID**: when many files share a prefix across calls, generate an ID + once (`SID=$(date +%s)-$$`), echo it, and reuse in later calls. +3. **Hardcoded names**: last resort — risks collisions when parallel calls or + subagents write to the same path. + +This protects against wasted round-trips when you need to retry parsing — for +example, if a `jq` filter fails or you extract the wrong field on the first +attempt. Re-read the file instead of hitting the server again. + +Do **not** duplicate the same **network** request in a shell pipeline (e.g. with +`||`) only to re-run `jq` or to reveal jq diagnostics—the duplicate call +adds load on JFrog without fetching new data. Run +`jq '' /tmp/jf-*-$$.json` (or redirect stdin from the file) instead +of re-running the same `jf api` or other identical network-backed command. + +Do **not** reuse saved output across unrelated steps or changed contexts (different +server, user, or intent). The file is only valid for the immediate sequence of +operations that motivated the original call. + +## When to read reference files + +Load the most specific file for the task at hand. Avoid loading more than 2-3 +reference files for a single operation — start with the most relevant one and +only load additional files if the first doesn't cover the need. File sizes +vary (~25–640 lines); larger files are noted with approximate line counts +below. + +### Cross-domain + +- **Disambiguating a JFrog entity, understanding entity types, or planning operations that span multiple products**: read `references/jfrog-entity-index.md`, then follow pointers to the relevant domain file +- **Looking up documentation URLs**: read `references/jfrog-url-references.md` + +### Artifactory + +- **Repository types, artifacts, builds, properties, or permission targets (concepts)**: read `references/artifactory-entities.md` (~220 lines) +- **Stored packages, package versions, version locations, or the metadata layer over Artifactory (concepts)**: read `references/stored-packages-entities.md` (~165 lines) +- **Repo, file, build, permission, user/group, or replication operations**: read `references/artifactory-operations.md` (for **listing builds** with a known project key: REST `GET /api/build?project=`, then `GET /api/build/?project=` — see § *Listing builds when the project key is known*) +- **AQL queries**: read `references/artifactory-aql-syntax.md` (~585 lines) +- **Artifactory REST beyond the CLI, structured JSON templates (replacing interactive wizards), or any Artifactory API gap**: read `references/artifactory-api-gaps.md` (~220 lines) + +### Xray & security + +- **Watches, policies, violations, components, or vulnerability scanning (concepts)**: read `references/xray-entities.md` (~290 lines) +- **Exposures scanning results (secrets, IaC, service misconfigurations, application security risks)**: read `references/xray-entities.md` § Exposures (Advanced Security) +- **Curation audit events (approved/blocked packages, dry-run policy evaluations, curation export)**: read `references/xray-entities.md` § Curation audit events + +### Release lifecycle & distribution + +- **Release bundles, lifecycle stages, distribution, or evidence (concepts)**: read `references/release-lifecycle-entities.md` (~180 lines) +- **Applications, application versions, releasables, promotions, or AppTrust (concepts)**: read `references/apptrust-entities.md` (~155 lines) + +### Catalog + +- **Public or custom catalog, package metadata, vulnerability advisories, licenses, OpenSSF, or MCP services (concepts)**: read `references/catalog-entities.md` (~190 lines) +- **CVE details, vulnerability lookup by CVE ID, or severity/affected-packages/fix-versions for a specific CVE**: go directly to `references/onemodel-query-examples.md` § *Public security domain* for the `searchVulnerabilities` query shape — this is self-contained; do not load the `jfrog-package-safety-and-download` skill for pure CVE lookups + +### OneModel (GraphQL) + +- **GraphQL queries** (applications, packages, evidence, release bundles, catalog, cross-domain, or "list/search my" platform entities): read `references/onemodel-graphql.md` (~325 lines) +- **Query templates and domain-specific examples**: read `references/onemodel-query-examples.md` (~555 lines) +- **Pagination, filtering, GraphQL variables, or date formatting**: read `references/onemodel-common-patterns.md` (~280 lines) + +### Platform administration + +- **Platform structure, project/repo membership, or project roles vs environments (concepts)**: read `references/platform-access-entities.md` +- **Access tokens, stats, projects, or system health**: read `references/platform-admin-operations.md` +- **Managing JFrog Projects, members, or environments**: read `references/projects-api.md` (~260 lines) +- **Platform REST beyond the CLI, or any platform-level API gap**: read `references/platform-admin-api-gaps.md` (~180 lines) + +### CLI setup & authentication + +- **Adding a server or logging in**: read `references/jfrog-login-flow.md` (~130 lines) +- **CLI not installed, upgrade needed, or `jq` unavailable**: read `references/jfrog-cli-install-upgrade.md` + +### General patterns + +- **Batching, parallel Shell calls, or launching subagents**: read `references/general-parallel-execution.md` (~135 lines) +- **Large or parallel data gathering, list-vs-detail APIs, sandbox/cache issues**: read `references/general-bulk-operations-and-agent-patterns.md` +- **Standalone HTML report with JFrog-aligned styling**: read `references/jfrog-brand-html-report.md` +- **Reusable gotchas from past tasks**: read or extend `references/general-use-case-hints.md` diff --git a/plugin/skills/jfrog/assets/.gitkeep b/plugin/skills/jfrog/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/plugin/skills/jfrog/references/apptrust-entities.md b/plugin/skills/jfrog/references/apptrust-entities.md new file mode 100644 index 0000000..6ca5088 --- /dev/null +++ b/plugin/skills/jfrog/references/apptrust-entities.md @@ -0,0 +1,154 @@ +# AppTrust entities + +When to read this file: + +- Working with **applications**, **application versions**, or **releasables**. +- Querying or managing **application version promotions** through stages. +- Understanding what **sources** (builds, release bundles, other app versions) feed into an application version. +- Using the OneModel GraphQL API with the `applications` query root. + +AppTrust entities are accessed exclusively via the **OneModel GraphQL API** +(`/onemodel/api/v1/graphql`). There are no CLI commands for this domain. + +For the OneModel query workflow (credentials, schema fetch, validation, +execution), read `references/onemodel-graphql.md`. + +## Entity relationship overview + +```mermaid +erDiagram + Application ||--o{ ApplicationVersion : "has versions" + ApplicationVersion ||--o{ Releasable : "contains" + ApplicationVersion ||--o{ Promotion : "promoted through" + ApplicationVersion }o--o{ Source : "assembled from" + Releasable }o--o{ Source : "contributed by" + Releasable ||--o{ Artifact : "contains" + Releasable }o--o| StoredPackageVersionLocation : "located at" + Application }o--o{ Owner : "owned by" + Application }o--o{ Label : "tagged with" + ApplicationVersion }o--o| EvidenceSubject : "attested by" +``` + +## Application + +The top-level entity representing a software application registered in +AppTrust. Applications belong to a JFrog Project and serve as the +organizational container for tracking versions, ownership, and criticality. + +| Field | Description | +|-------|-------------| +| `key` | Unique identifier (referenced as `applicationKey` or `appKey` elsewhere) | +| `projectKey` | JFrog Project this application belongs to | +| `displayName` | Human-readable name | +| `criticality` | `unspecified`, `low`, `medium`, `high`, `critical` | +| `maturityLevel` | `unspecified`, `experimental`, `production`, `end_of_life` | +| `owners` | List of users or groups that own the application | +| `labels` | Key-value pairs for custom categorization | + +Query: `applications.getApplication(key: "...")` or +`applications.searchApplications(where: {...})`. + +## Application version + +A versioned instance of an application. Each version captures a specific set +of releasable artifacts, their sources, and a promotion history through +lifecycle stages. + +| Field | Description | +|-------|-------------| +| `application` | Parent application | +| `version` | Version identifier (semantic or custom) | +| `tag` | Optional tag | +| `status` | Processing status: `STARTED`, `FAILED`, `COMPLETED`, `DELETING` | +| `releaseStatus` | Release maturity: `PRE_RELEASE`, `RELEASED`, `TRUSTED_RELEASE` | +| `currentStageName` | Most recent stage the version has been promoted to (null if never promoted) | +| `createdBy`, `createdAt` | Audit fields | +| `evidenceSubject` | Evidence attestation anchor (shared across domains) | + +The `releaseStatus` field is distinct from `status`: `status` tracks the +version creation process, while `releaseStatus` tracks its release maturity. + +Query: `applications.getApplicationVersion(applicationKey: "...", version: "...")` +or `applications.searchApplicationVersions(where: {...})`. + +## Releasable + +A deployable unit within an application version — either a **package version** +or an individual **artifact**. + +| Field | Description | +|-------|-------------| +| `name` | Package name or artifact file name | +| `version` | Package version (empty for non-package artifacts) | +| `packageType` | Repository package type (docker, maven, generic, etc.) | +| `releasableType` | `artifact` or `package_version` | +| `sha256` | Leading file checksum (e.g. manifest for Docker images) | +| `totalSize` | Sum of all artifact sizes in bytes | +| `sources` | Sources that contributed to this releasable | +| `artifacts` | Individual files that make up the releasable | +| `packageVersionLocation` | Link to `StoredPackageVersionLocation` for package releasables | +| `vcsCommit` | VCS commit details (for AppTrust-bound package versions) | + +Releasables bridge the application model to the underlying Artifactory +storage. The `packageVersionLocation` field connects to the Stored Packages +domain (see `stored-packages-entities.md`). + +## Application version promotion + +Records the promotion of an application version from one stage to another. +All promotions are recorded including failed attempts. + +| Field | Description | +|-------|-------------| +| `sourceStageName` | Stage being promoted from (empty for first promotion) | +| `targetStageName` | Stage being promoted to | +| `status` | `SUBMITTED`, `STARTED`, `PENDING`, `COMPLETED`, `FAILED`, `REJECTED` | +| `createdBy`, `createdAt` | Who initiated and when | +| `artifacts` | Artifacts included in this promotion (repo + path) | +| `messages` | Error messages if the promotion failed | + +Promotions use the same environment/stage model as Release Bundle promotions +(see `release-lifecycle-entities.md`) but at the application level. + +## Sources + +Sources describe how releasables were assembled into an application version. +Four types exist: + +| Source type | Fields | Description | +|-------------|--------|-------------| +| **Build** | `name`, `number`, `startedAt`, `repositoryKey` | A CI/CD build that produced releasables | +| **ReleaseBundle** | `name`, `version` | A release bundle whose artifacts were included | +| **ApplicationVersion** | `applicationKey`, `version` | Another application version (composition) | +| **Direct** | (none) | Directly included without an associated build or bundle | + +Sources appear at both the application version level (all sources) and the +individual releasable level (sources for that specific releasable). + +## Artifacts (within application versions) + +Individual files within releasables. + +| Field | Description | +|-------|-------------| +| `filePath` | Path in the repository (excluding repo key) | +| `downloadPath` | Full path for downloading from a Release Bundle repository | +| `sha256` | Checksum | +| `size` | Size in bytes | +| `evidenceSubject` | Evidence attestation anchor | + +## Cross-domain connections + +AppTrust entities connect to other domains via the OneModel GraphQL API: + +- **Evidence** — `ApplicationVersion.evidenceSubject` and + `ApplicationVersionArtifact.evidenceSubject` link to the Evidence domain + via `EvidenceSubject.fullPath`. This allows querying evidence attached to + app versions and their artifacts. +- **Stored Packages** — `Releasable.packageVersionLocation` links to + `StoredPackageVersionLocation`, connecting the application model to where + packages physically reside in Artifactory. +- **Release Bundles** — source type `ReleaseBundle` references release bundle + name/version from the Release Lifecycle domain. +- **Builds** — source type `Build` references build-info records from + Artifactory. diff --git a/plugin/skills/jfrog/references/artifactory-api-gaps.md b/plugin/skills/jfrog/references/artifactory-api-gaps.md new file mode 100644 index 0000000..1533d8a --- /dev/null +++ b/plugin/skills/jfrog/references/artifactory-api-gaps.md @@ -0,0 +1,206 @@ +# Artifactory API Gaps + +Operations available through REST API but not through CLI commands. +Invoke them via `jf api [flags]` (authentication is handled +automatically against the active `jf config` server; see the base skill's +*Invoking platform APIs with `jf api`* section). + +## Repository management + +### Get repository configuration +```bash +jf api /artifactory/api/repositories/ +``` +Returns the full JSON configuration of a repository. Useful as a template +for creating similar repos. + +### List all repositories +```bash +jf api /artifactory/api/repositories +``` +Optional query params (combinable): `type` (one of `local`, `remote`, +`virtual`, `federated`), `packageType` (e.g. `docker`, `maven`, `npm`, +`pypi`, `generic`), `project`. Examples: +```bash +jf api "/artifactory/api/repositories?type=local" +jf api "/artifactory/api/repositories?packageType=docker" +jf api "/artifactory/api/repositories?type=remote&packageType=maven&project=my-project" +``` + +### Get repositories (v2) +```bash +jf api /artifactory/api/repositories/configurations +``` +Optional query params (combinable, comma-separated values allowed): +`repoType` (case-insensitive; one of `local`, `remote`, `virtual`, +`federated`) and `packageType` (e.g. `maven`, `docker`, `npm`). Note: +`repo_type` is silently ignored — the correct name is `repoType`. +Examples: +```bash +jf api "/artifactory/api/repositories/configurations?repoType=local" +jf api "/artifactory/api/repositories/configurations?packageType=maven" +jf api "/artifactory/api/repositories/configurations?repoType=local,remote&packageType=docker" +``` + +### Check if repository exists +```bash +jf api /artifactory/api/repositories/ -X HEAD +# 200 = exists, 400 = does not exist +``` + +## Storage and system + +### Get storage summary +```bash +jf api /artifactory/api/storageinfo +``` + +### Refresh storage summary +```bash +jf api /artifactory/api/storageinfo/calculate -X POST +``` + +### Get storage item info +```bash +jf api "/artifactory/api/storage//" +``` + +### System ping +```bash +jf api /artifactory/api/system/ping +``` + +### System version +```bash +jf api /artifactory/api/system/version +``` + +### System configuration +```bash +jf api /artifactory/api/system/configuration +``` + +## Search (beyond CLI) + +### AQL queries +```bash +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" \ + -d 'items.find({"repo":"my-repo","name":{"$match":"*.jar"}})' +``` + +For remote repository content, query the `-cache` suffixed repo: +```bash +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" \ + -d 'items.find({"repo":"my-remote-cache"})' +``` + +### Property search +```bash +jf api "/artifactory/api/search/prop?key=value&repos=my-repo" +``` + +### Checksum search +```bash +jf api "/artifactory/api/search/checksum?sha256=" +``` + +### GAVC search (Maven) +```bash +jf api "/artifactory/api/search/gavc?g=com.example&a=mylib&v=1.0" +``` + +## User and group management + +User and group operations are handled by the Access service. See +`platform-admin-api-gaps.md` (Users / Groups sections) for the full set. + +## Metadata calculation + +Trigger metadata recalculation for various package types: +```bash +# Maven +jf api /artifactory/api/maven/calculateMetaData/ -X POST + +# npm +jf api /artifactory/api/npm//reindex -X POST + +# Docker +# (automatic, no manual trigger) + +# PyPI +jf api /artifactory/api/pypi//reindex -X POST + +# Helm +jf api /artifactory/api/helm//reindex -X POST + +# Debian +jf api /artifactory/api/deb/reindex/ -X POST +``` + +## Trash can and garbage collection + +### Empty trash +```bash +jf api /artifactory/api/trash/empty -X POST +``` + +### Restore from trash +```bash +jf api "/artifactory/api/trash/restore//" -X POST +``` + +### Run garbage collection +```bash +jf api /artifactory/api/system/storage/gc -X POST +``` + +## Federated repositories (beyond basic CRUD) + +### Get federation status +```bash +jf api /artifactory/api/federation/status/ +``` + +### Trigger full sync +```bash +jf api "/artifactory/api/federation/fullSyncAll/" -X POST +``` + +## Build info (beyond CLI) + +### List builds (prefer scoped queries) + +**Unscoped** `GET /artifactory/api/build` (no query parameters) can **time +out** on busy instances. Prefer **project-scoped** or **repo-scoped** +listing, then detail GETs. Full flow: read `artifactory-operations.md` +§ *Listing builds when the project key is known*. + +```bash +# Project scope — build names (latest per name) +jf api "/artifactory/api/build?project=" + +# Project scope — all run numbers for one build name (response: buildsNumbers) +jf api "/artifactory/api/build/?project=" + +# Build-info repo scope — alternative when you know the repo key +jf api "/artifactory/api/build?buildRepo=" +``` + +### Get build info +```bash +# Default build-info repo only (no project / non-default repo) +jf api "/artifactory/api/build//" + +# Project or custom build-info repo +jf api "/artifactory/api/build//?project=" +jf api "/artifactory/api/build//?buildRepo=" +``` + +### Delete builds +```bash +jf api /artifactory/api/build/delete \ + -X POST -H "Content-Type: application/json" \ + -d '{"buildName":"my-build","buildNumbers":["1","2"]}' +``` diff --git a/plugin/skills/jfrog/references/artifactory-aql-syntax.md b/plugin/skills/jfrog/references/artifactory-aql-syntax.md new file mode 100644 index 0000000..b120275 --- /dev/null +++ b/plugin/skills/jfrog/references/artifactory-aql-syntax.md @@ -0,0 +1,656 @@ +# AQL (Artifactory Query Language) + +AQL queries are sent as POST requests with `Content-Type: text/plain`: + +```bash +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" -d '' +``` + +## Query structure + +``` +.find() + .include() + .sort() + .offset() + .limit() + .distinct() +``` + +Only `.find()` is required. The others are optional and chainable. +**The chain order above is enforced by the server.** `.include()` must come +before `.sort()`, `.sort()` before `.offset()`, etc. Putting them out of +order (e.g. `.sort()` before `.include()`) produces a parse error. + +**Mandatory include fields:** `items` requires `"repo","path","name"`; +`builds` requires `"name","number","repo"`. Always include these even when +you only need a subset — narrow results with `jq` post-query instead: + +``` +items.find({"name":"commons-lang3-3.12.0.jar"}) + .include("repo","path","name") + .distinct(true) +``` + +## Domains + +AQL has 13 queryable domains. Each domain represents a different entity type +and has its own set of fields. + + +| Domain | Query name | Description | +| -------------------- | ------------------- | ---------------------------------------------- | +| Items | `items` | Artifacts stored in repositories (most common) | +| Properties | `properties` | Key-value properties on items | +| Item infos | `item.infos` | Property modification metadata | +| Statistics | `stats` | Download statistics (local and remote) | +| Builds | `builds` | Build info records | +| Build modules | `modules` | Modules within a build | +| Build artifacts | `artifacts` | Artifacts produced by a build module | +| Build dependencies | `dependencies` | Dependencies consumed by a build module | +| Build properties | `build.properties` | Key-value properties on builds | +| Build promotions | `build.promotions` | Build promotion records | +| Module properties | `module.properties` | Key-value properties on build modules | +| Release bundles | `releases` | Release bundle records | +| Release bundle files | `release_artifacts` | Files within a release bundle | + + +## Domain relationships + +Domains connect through the following join paths. Cross-domain queries +traverse these links — fields from related domains can appear in criteria +and include clauses by prefixing the domain path. + +```mermaid +erDiagram + items ||--o{ properties : "has" + items ||--o| item_infos : "has" + items ||--o{ stats : "has" + items ||--o{ artifacts : "via checksum" + items ||--o{ dependencies : "via checksum" + items ||--o{ release_artifacts : "has" + artifacts }o--|| modules : "belongs to" + dependencies }o--|| modules : "belongs to" + modules }o--|| builds : "belongs to" + modules ||--o{ module_properties : "has" + builds ||--o{ build_properties : "has" + builds ||--o{ build_promotions : "has" + release_artifacts }o--|| releases : "belongs to" +``` + + + +**Key:** Items connect to build artifacts and dependencies through SHA-1 +checksum matching, not a direct key. This means a cross-domain query from +items to builds traverses: items → artifacts → modules → builds. + +### Cross-domain field paths + +To reference a field from a related domain, use dot-separated domain paths: + +``` +items.find({"artifact.module.build.name":"my-build"}) + .include("name","repo","path","artifact.module.build.number") +``` + +Common cross-domain paths from items: + +- `stat.downloads`, `stat.downloaded` — download statistics +- `property.key`, `property.value` — item properties +- `artifact.module.build.name` — build that produced the item +- `artifact.module.build.number` — build number + +From builds: + +- `module.artifact.name` — artifacts in build modules +- `module.dependency.name` — dependencies of build modules + +## Fields by domain + +Field types: `string`, `date`, `int`, `long`, `itemType` (`file`, `folder`, +or `any`). Fields marked "default" are returned without explicit `.include()`. + +### items + + +| Field | Type | Default | +| --------------- | -------- | ------- | +| `repo` | string | yes | +| `path` | string | yes | +| `name` | string | yes | +| `type` | itemType | yes | +| `size` | long | yes | +| `depth` | int | yes | +| `created` | date | yes | +| `created_by` | string | yes | +| `modified` | date | yes | +| `modified_by` | string | yes | +| `updated` | date | yes | +| `actual_md5` | string | no | +| `actual_sha1` | string | no | +| `sha256` | string | no | +| `original_md5` | string | no | +| `original_sha1` | string | no | + + +Computed field: `virtual_repos` — returns virtual repositories that include +the item's actual repository. Must use `.include("virtual_repos")` explicitly; +requires `repo`, `path`, `name` in the result set. + +### properties + + +| Field | Type | Default | +| ------- | ------ | ------- | +| `key` | string | yes | +| `value` | string | yes | + + +### stats + + +| Field | Type | Default | +| ---------------------- | ------ | ------- | +| `downloads` | int | yes | +| `downloaded` | date | yes | +| `downloaded_by` | string | yes | +| `remote_downloads` | int | yes | +| `remote_downloaded` | date | yes | +| `remote_downloaded_by` | string | yes | +| `remote_origin` | string | yes | +| `remote_path` | string | yes | + + +### item.infos + + +| Field | Type | Default | +| ------------------- | ------ | ------- | +| `props_modified` | date | yes | +| `props_modified_by` | string | yes | +| `props_md5` | string | yes | + + +### builds + + +| Field | Type | Default | +| ------------- | ------ | ------- | +| `url` | string | yes | +| `name` | string | yes | +| `number` | string | yes | +| `started` | date | yes | +| `created` | date | yes | +| `created_by` | string | yes | +| `modified` | date | yes | +| `modified_by` | string | yes | +| `repo` | string | no | + + +### modules + + +| Field | Type | Default | +| ------ | ------ | ------- | +| `name` | string | yes | + + +### artifacts + + +| Field | Type | Default | +| ------ | ------ | ------- | +| `name` | string | yes | +| `type` | string | yes | +| `sha1` | string | yes | +| `md5` | string | yes | + + +### dependencies + + +| Field | Type | Default | +| ------- | ------ | ------- | +| `name` | string | yes | +| `scope` | string | yes | +| `type` | string | yes | +| `sha1` | string | yes | +| `md5` | string | yes | + + +### build.properties + + +| Field | Type | Default | +| ------- | ------ | ------- | +| `key` | string | yes | +| `value` | string | yes | + + +### build.promotions + + +| Field | Type | Default | +| ------------ | ------ | ------- | +| `created` | date | yes | +| `created_by` | string | yes | +| `status` | string | yes | +| `repo` | string | yes | +| `comment` | string | yes | +| `user` | string | yes | + + +### module.properties + + +| Field | Type | Default | +| ------- | ------ | ------- | +| `key` | string | yes | +| `value` | string | yes | + + +### releases + + +| Field | Type | Default | +| -------------- | --------------------------- | ------- | +| `name` | string | yes | +| `version` | string | yes | +| `status` | string | yes | +| `created` | date | yes | +| `signature` | string | yes | +| `type` | string (`SOURCE`, `TARGET`) | yes | +| `storing_repo` | string | yes | + + +### release_artifacts + + +| Field | Type | Default | +| ------ | ------ | ------- | +| `path` | string | yes | + + +## Comparators + + +| Operator | Meaning | Example | +| ---------- | -------------------------------- | ------------------------------------ | +| `$eq` | Equals (default if omitted) | `{"type":"file"}` | +| `$ne` | Not equals | `{"type":{"$ne":"folder"}}` | +| `$eqic` | Equals, case-insensitive | `{"name":{"$eqic":"README.md"}}` | +| `$match` | Wildcard match (`*`, `?`) | `{"name":{"$match":"*.jar"}}` | +| `$matchic` | Wildcard match, case-insensitive | `{"name":{"$matchic":"*.JAR"}}` | +| `$nmatch` | Wildcard not-match | `{"name":{"$nmatch":"*-SNAPSHOT*"}}` | +| `$gt` | Greater than | `{"size":{"$gt":"1000000"}}` | +| `$gte` | Greater than or equal | `{"stat.downloads":{"$gte":"10"}}` | +| `$lt` | Less than | `{"size":{"$lt":"5000"}}` | +| `$lte` | Less than or equal | `{"modified":{"$lte":"2025-01-01"}}` | + + +### Boolean operators + + +| Operator | Description | +| -------- | ---------------------------------------------------------------------- | +| `$and` | All conditions must match (implicit when fields are at the same level) | +| `$or` | Any condition must match | + + +``` +items.find({"$and":[ + {"repo":"my-repo"}, + {"$or":[ + {"name":{"$match":"*.jar"}}, + {"name":{"$match":"*.war"}} + ]} +]}) +``` + +### Relative date comparators + +AQL supports relative date queries with `$last` and `$before`: + + +| Operator | Meaning | Example | +| --------- | ------------------------------------------------------- | ------------------------------- | +| `$last` | Within the last N period (equivalent to `$gt` from now) | `{"modified":{"$last":"7d"}}` | +| `$before` | Before the last N period (equivalent to `$lt` from now) | `{"created":{"$before":"3mo"}}` | + + +Supported units: `d` (days), `w` (weeks), `mo` (months), `y` (years), +`s` (seconds), `mi` (minutes), `ms` (milliseconds). + +### Multi-property AND + +To match items that have property A=1 **and** property B=2 (different +property rows), use `$and` with `@` shorthand: + +``` +items.find({"$and":[ + {"@build.name":"my-build"}, + {"@build.number":"42"} +]}) +``` + +AQL also documents a `$msp` (multi-set property) operator for this purpose, +but `$msp` is **unreliable in practice** — it returns 0 results on many +server versions even when matching items exist. Prefer `$and` with `@` +shorthand, which is verified to work correctly. + +## Date queries + +Dates use ISO 8601 format for absolute dates: + +``` +items.find({"modified":{"$gt":"2025-06-01T00:00:00.000Z"}}) +``` + +Or use relative dates (preferred — avoids hardcoding timestamps): + +``` +items.find({"modified":{"$last":"30d"}}) +items.find({"created":{"$before":"6mo"}}) +``` + +## Property queries + +Two equivalent syntaxes for property filtering: + +**`@key` shorthand** — concise, works for single property conditions: + +``` +items.find({"repo":"my-repo","@build.name":"my-build","type":"file"}) +``` + +**Explicit form** — `property.key`/`property.value` pairs: + +``` +items.find({ + "repo":"my-repo", + "property.key":"build.name", + "property.value":"my-build" +}) +``` + +**Multi-property AND** — use `$and` with `@` shorthand to match across +different property rows: + +``` +items.find({"$and":[ + {"@build.name":"my-build"}, + {"@build.number":"42"} +]}) +``` + +> **Note:** The `@key` shorthand works inside `$and`. For `$or`, use the +> explicit `property.key`/`property.value` form if the shorthand does not +> return expected results. + +## Include + +Select which fields to return. Without `.include()`, AQL returns each +domain's default field set. + +**When you use `.include()`, you replace the defaults — so you must +explicitly list any required fields:** + +- `items` domain: always include `"repo","path","name"` (server rejects +the query otherwise) +- `builds` domain: always include `"name","number","repo"` + +``` +items.find({"repo":"my-repo"}) + .include("name","repo","path","size","sha256","stat.downloads") +``` + +Cross-domain includes use dot-separated paths: + +``` +items.find({"repo":"my-repo"}) + .include("name","repo","path","property.key","property.value") +``` + +## Sort and pagination + +``` +items.find({"repo":"my-repo"}) + .sort({"$desc":["modified"]}) + .offset(0) + .limit(50) +``` + +Sort directions: `$asc`, `$desc`. Sort fields must also appear in the result +set (explicit `.include()` or default fields). See +[Before constructing a query](#before-constructing-a-query) for sort +performance rules. + +## Distinct + +Deduplicate result rows: + +``` +items.find({"repo":"my-repo"}).distinct(true) +``` + +## Validation rules + +The server enforces these constraints — violating them produces an error: + +**Non-admin users:** + +- `items` domain queries must include `repo`, `path`, `name` in results +(needed for permission filtering) +- `builds` domain queries must include `name`, `number`, `repo` in results + +**Transitive mode** (`.transitive()` for querying through virtual repos): + +- Only works with `items` domain +- Include subdomains limited to `items` and `properties` +- Repo criteria must use `$eq` (exact match) with a single repository +- No `offset` or `sort` allowed + +## Before constructing a query + +Run through these checks before writing any AQL query: + +1. **Never `.sort()` without a `repo` filter** — forces a full table scan + across all repositories. Sort client-side with `jq` instead. Also, + `.sort()` on cross-domain fields (e.g. `stat.downloads` in `items.find()`) + is silently ignored — fetch all rows and sort client-side. +2. **Always set `.limit()`** — no built-in default limit; unbounded queries + can time out or OOM. Broad queries without a `repo` filter are especially + expensive. +3. **`range.total` = returned count, not total matching** — AQL has no + count-only mode. To find the true total, paginate with `.offset()` until + a page returns fewer results than the limit. +4. **AQL has no repo-type field** — to restrict to local repos, either + pre-query `GET /api/repositories?type=local` and add repo names to + criteria (practical when count is small), or query without a repo filter + and exclude `-cache` / `-virtual` suffixed repos client-side with `jq`. +5. **Narrow server-side first** — add every applicable filter (`created_by`, + `created`, `type`, `name`) before relying on client-side `jq` filtering. + +## Common query patterns + +### Find all JARs in a repo + +``` +items.find({"repo":"libs-release","name":{"$match":"*.jar"}}) +``` + +### Find large files (> 100 MB) + +``` +items.find({"repo":"my-repo","size":{"$gt":"104857600"},"type":"file"}) +``` + +### Find Maven SNAPSHOT JARs + +Use `*-SNAPSHOT*.jar` (not `*-SNAPSHOT.jar`) to also match classifier +artifacts like `-sources.jar` and `-javadoc.jar`: + +``` +items.find({"repo":"libs-snapshot","name":{"$match":"*-SNAPSHOT*.jar"},"type":"file"}) +``` + +### Find artifacts modified in the last 7 days + +``` +items.find({"repo":"my-repo","modified":{"$last":"7d"},"type":"file"}) + .sort({"$desc":["modified"]}) + .limit(100) +``` + +### Docker queries + +Use `"name":"manifest.json"` to **list tags** (one per tag). Use +`"name":{"$match":"*manifest.json"}` to **query all manifests** (includes +`list.manifest.json` for multi-arch tags — see [Gotchas](#gotchas)). + +``` +items.find({"repo":"docker-local","path":{"$match":"my-image/*"},"name":"manifest.json"}) +``` + +### Docker image size + +**Do not use AQL** — layer blobs live at `/sha256:/`, not +under `//`. Use the V2 manifest API (returns `layers[].size`): + +```bash +jf api "/artifactory/api/docker//v2//manifests/" \ + -H "Accept: application/vnd.docker.distribution.manifest.v2+json" +``` + +For multi-arch images the response is an image index; fetch each platform +manifest by digest to get its layers. + +### Find artifacts with a specific property + +``` +items.find({"repo":"my-repo","@build.name":"my-build","type":"file"}) +``` + +### Find never-downloaded files (zero download count) + +Zero-download items lack a stats row — filter client-side instead +(see [Gotchas](#gotchas)): + +```bash +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" -d ' +items.find({"repo":"my-repo","type":"file"}) + .include("repo","path","name","size","stat.downloads") +' | jq '[.results[] | select((.stats[0].downloads // 0) == 0) | {repo, path, name, size}]' +``` + +### Find artifacts not downloaded in 90 days + +Only matches previously-downloaded items (see [Gotchas](#gotchas)). +Combine with the never-downloaded pattern above for full coverage. + +``` +items.find({ + "repo":"my-repo", + "type":"file", + "stat.downloaded":{"$before":"90d"} +}).include("name","repo","path","stat.downloaded","size") +``` + +### Find items by build name (cross-domain) + +``` +items.find({"artifact.module.build.name":"my-service"}) + .include("name","repo","path","artifact.module.build.number") + .sort({"$desc":["modified"]}) + .limit(50) +``` + +### Find builds by name + +Non-admin users must include `name`, `number`, `repo` — omitting any +produces an error. + +``` +builds.find({"name":{"$match":"*my-service*"}}) + .include("name","number","repo","started") + .sort({"$desc":["started"]}) + .limit(10) +``` + +### Find build artifacts + +``` +artifacts.find({"module.build.name":"my-service","module.build.number":"42"}) + .include("name","type","sha1","md5") +``` + +### Find build dependencies + +``` +dependencies.find({"module.build.name":"my-service","module.build.number":"42"}) + .include("name","scope","type","sha1") +``` + +### Remote repository content + +Remote repo artifacts are stored in a `-cache` suffixed repo. Always query +the cache repo, not the remote repo itself: + +``` +items.find({"repo":"npm-remote-cache","name":{"$match":"*.tgz"}}) +``` + +## Gotchas + +- The request body is **plain text**, not JSON — use +`Content-Type: text/plain`. +- String values in criteria must be quoted, including numeric comparisons +(`"size":{"$gt":"1000"}` not `"size":{"$gt":1000}`). +- Remote repo content lives in `-cache`, not ``. +- Sort fields must appear in the result set (included explicitly or by +default). +- Non-admin `items` queries must return `repo`, `path`, `name`. +- Non-admin `builds` queries must return `name`, `number`, `repo`. +- Items connect to builds through checksum matching (SHA-1), so cross-domain +queries between items and builds are valid but traverse multiple joins. +- The `path` value for items at the **root** of a repository is `"."`, not +`""` or `"/"`. Use `"path":"."` to match root-level files. +- **Docker `list.manifest.json`** — multi-arch images store two manifest files per + tag: `manifest.json` (platform-specific manifest) and `list.manifest.json` (OCI + image index). Filtering by `"name":"manifest.json"` is correct for tag listing + (one result per tag), but silently excludes `list.manifest.json` entries. Use + `"name":{"$match":"*manifest.json"}` when querying by uploader, date range, or + any context where all manifest pushes should be counted. +- **`stat.downloads` filters do not match zero-download items** — never-downloaded + items lack a stats row so the join finds nothing. Use the client-side `jq` + approach in "Find never-downloaded files" above. +- `$match` uses SQL-style wildcards: `*` matches any characters, `?` matches +exactly one character. It is **not** regex. Literal `_` and `%` in patterns +are escaped automatically. +- The `builds.number` field is a **string**, not an integer. Build numbers +like `"42"`, `"1.0.3"`, and `"SNAPSHOT-1"` are all valid. +- Release bundle `type` values are uppercase strings: `"SOURCE"` or +`"TARGET"`. +- Dates accept both ISO 8601 format (`"2025-06-01T00:00:00.000Z"`) and +epoch milliseconds as a string (`"1719792000000"`). +- The server silently excludes trash, support-bundle, and in-transit +repository content from AQL results. If an item exists but doesn't appear +in results, it may be in one of these hidden repos. +- Virtual repo queries are rewritten to search the underlying physical repos. +The `repo` field in results shows the physical repo name, not the virtual +repo name you queried. + +## Official documentation + +- [Artifactory Query Language](https://docs.jfrog.com/artifactory/docs/artifactory-query-language) — overview and architecture +- [Query Structure and Syntax](https://docs.jfrog.com/artifactory/docs/aql-syntax) — domain queries, field references, JSON-like syntax rules +- [Search Criteria and Operators](https://docs.jfrog.com/artifactory/docs/aql-search-criteria) — comparators, wildcards, `$msp`, relative time +- [AQL Entities and Fields Reference](https://docs.jfrog.com/artifactory/docs/aql-entities-fields-reference) — complete field list for all domains +- [Query Output and Modifiers](https://docs.jfrog.com/artifactory/docs/aql-query-output) — `.include()`, `.sort()`, `.offset()`, `.limit()`, `.distinct()` +- [Query Execution and Permissions](https://docs.jfrog.com/artifactory/docs/aql-query-execution) — authentication, scoped tokens, HTTP errors, streaming +- [AQL Examples and Common Patterns](https://docs.jfrog.com/artifactory/docs/aql-examples) — ready-to-use queries by use case +- [Repository-Specific Queries](https://docs.jfrog.com/artifactory/docs/aql-repository-queries) — `.transitive()`, virtual repos, remote search +- [Performance and Operational Controls](https://docs.jfrog.com/artifactory/docs/aql-performance) — result limits, timeouts, rate limiting, optimization + diff --git a/plugin/skills/jfrog/references/artifactory-entities.md b/plugin/skills/jfrog/references/artifactory-entities.md new file mode 100644 index 0000000..1e4d950 --- /dev/null +++ b/plugin/skills/jfrog/references/artifactory-entities.md @@ -0,0 +1,236 @@ +# Artifactory entities + +When to read this file: + +- Working with **repositories** and you need to understand the difference between local, remote, virtual, and federated types. +- Managing **artifacts**, **properties**, or **package types**. +- Working with **builds**, **build promotion**, or **permission targets**. +- Debugging unexpected behavior related to repo types (e.g. upload failures, missing search results). + +For CLI commands see `artifactory-operations.md`. For API gaps see +`artifactory-api-gaps.md`. For AQL syntax see `artifactory-aql-syntax.md`. + +## Repositories + +A repository is the primary storage and resolution unit in Artifactory. Every +repo has a **key** (unique identifier), a **package type** (immutable after +creation), and a **repository class** (`rclass`) that determines its behavior. + +### Repository types + +| Type | `rclass` | Behavior | Stores artifacts? | +|------|----------|----------|-------------------| +| **Local** | `local` | Hosts artifacts deployed directly (upload, promote, copy, move) | Yes | +| **Remote** | `remote` | Proxies an external URL; downloads are cached in a companion `-cache` repo | Only in the `-cache` repo | +| **Virtual** | `virtual` | Aggregates multiple local and remote repos under a single URL for resolution | No (resolves from underlying repos) | +| **Federated** | `federated` | Local repo that bi-directionally synchronizes across Platform Deployments | Yes (replicated across sites) | + +### Key relationships and fields + +- `key` — unique repo identifier (e.g. `libs-release-local`) +- `packageType` — determines layout and protocol (see Package types below) +- `rclass` — `local`, `remote`, `virtual`, or `federated` +- `url` — (remote only) the external source URL being proxied +- `repositories` — (virtual only) ordered list of local/remote repos to aggregate +- `projectKey` — links repo to a JFrog Project (see `platform-access-entities.md`) +- `environments` — environments the repo is assigned to (used in RBAC and lifecycle) + +### System repositories + +Artifactory and Xray maintain several **system repositories** for internal +platform metadata. These are not user-created and should be excluded when +iterating over repositories for reporting, scanning, or auditing: + +| Pattern | Purpose | +|---------|---------| +| `release-bundles` | Release Bundles V1 metadata | +| `release-bundles-v2` | Release Bundles V2 metadata | +| `artifactory-build-info` | Default build info storage | +| `*-release-bundles` | Project-scoped Release Bundles V1 | +| `*-release-bundles-v2` | Project-scoped Release Bundles V2 | +| `*-build-info` | Project-scoped build info storage | +| `*-application-versions` | AppTrust application version metadata | + +Including these in aggregate queries (violation counts, storage reports, etc.) +produces misleading results because they contain platform metadata rather than +user artifacts. + +### Remote repository cache + +When Artifactory downloads an artifact through a remote repo, it stores the +cached copy in a **separate local repo** named `-cache`. This is +critical for: + +- **AQL queries** — search the `-cache` repo, not the remote repo key +- **Properties** — properties on cached artifacts live on the `-cache` repo +- **Storage calculations** — cached artifacts consume storage under the `-cache` repo + +The remote repo key itself is used for **configuration** (URL, credentials, +inclusion/exclusion patterns) but does not directly contain artifacts. + +### Virtual repository resolution + +A virtual repo aggregates **both local and remote repos** under a single URL. +It resolves artifacts by searching its underlying repos in the configured +**order** — when the same artifact exists in multiple underlying repos, the +first match wins. + +A virtual repo may designate one of its underlying **local** repos as the +**default deployment repository**. Uploads through the virtual URL are routed +to that local repo. Without a default deployment repo, the virtual repo is +read-only. + +```mermaid +erDiagram + VirtualRepo ||--o{ LocalRepo : "aggregates" + VirtualRepo ||--o{ RemoteRepo : "aggregates" + VirtualRepo ||--o| LocalRepo : "defaultDeploymentRepo" + RemoteRepo ||--|| CacheRepo : "has -cache" +``` + +## Artifacts + +An artifact is a file stored in a repository. Each artifact is uniquely +identified by the triple **repo + path + name**. + +Key attributes: +- `repo`, `path`, `name` — location identifier +- `size` — bytes +- `sha256`, `sha1`, `md5` — checksums (sha256 is the primary identifier for cross-referencing with builds and Xray) +- `created`, `modified`, `created_by`, `modified_by` — audit fields + +Artifacts are **content-addressable** — build info and Xray reference them by +checksum, not by path. Moving or copying an artifact changes its path but not +its checksum, so build associations follow the artifact. + +## Properties + +Key-value metadata pairs attached to artifacts or folders. + +- Keys are strings; values are strings or arrays of strings +- Set via `jf rt set-props`, queried via AQL or the properties API +- Commonly used for: build metadata, maturity labels, promotion tracking, cleanup policies +- Properties on remote-cached artifacts live on the `-cache` repo + +## Package types + +The `packageType` field on a repository determines how Artifactory interprets +its contents. It controls directory structure conventions, metadata extraction, +and which client protocols are supported (e.g. Docker registry API, npm +registry, Maven layout). + +Common types: `maven`, `gradle`, `npm`, `docker`, `pypi`, `nuget`, `go`, +`helm`, `rpm`, `debian`, `generic`. + +Package type is **immutable** — it cannot be changed after repo creation. Use +`generic` when no specific package type applies. + +## Build info + +A build info record captures CI/CD metadata: which artifacts were produced, +which dependencies were consumed, and the build environment. + +| Field | Description | +|-------|-------------| +| `name` + `number` | Unique identifier for a build run | +| `modules` | List of modules, each with its own artifacts and dependencies | +| `vcs` | Version control metadata (revision, URL, branch) | +| `buildAgent`, `agent` | CI tool info | +| `properties` | Custom build-level properties | + +Build info references artifacts **by checksum** (sha256). This means: +- A build can reference artifacts across multiple repositories +- Moving an artifact does not break the build association +- Xray scans build info by resolving checksums to components + +Lifecycle: collect → publish → (optionally) promote → (optionally) scan. + +## Build promotion + +Promotion changes a build's **status** and can copy or move its artifacts +from a source repo to a target repo. + +| Field | Description | +|-------|-------------| +| `status` | Target status label (e.g. `staged`, `released`) | +| `sourceRepo` | Where artifacts currently reside | +| `targetRepo` | Where artifacts should be moved/copied | +| `copy` | If `true`, copy instead of move | + +Promotion records are queryable via AQL (`build.promotions` domain) and the +build promotion API. + +## Permissions + +Permissions define RBAC policies mapping **resources** and **principals** +(users and groups) to **actions**. Two models exist: + +### Permissions V2 (Access Permissions) — current model + +Managed by the **Access service** (since Artifactory 7.72.0, recommended from +7.77.2). Supports all resource types. + +| Component | Description | +|-----------|-------------| +| `name` | Permission name | +| `resources` | Map of resource type → targets + actions | + +Resource types: `artifact` (repositories), `build`, `release_bundle`, +`destination` (Edge nodes), `pipeline_source`. + +Each resource contains: +- `targets` — map of target names/patterns to include/exclude patterns +- `actions.users` — map of username → list of actions +- `actions.groups` — map of group name → list of actions + +Actions use uppercase: `READ`, `ANNOTATE`, `DEPLOY/CACHE`, `DELETE/OVERWRITE`, +`MANAGE_XRAY_METADATA`, `MANAGE`. + +API: `POST/PUT/GET/DELETE /access/api/v2/permissions/{permissionName}`. + +Documentation: [Permissions](https://docs.jfrog.com/administration/docs/permissions). + +### Permission targets (V1) — legacy model + +Managed by **Artifactory**. Still functional and backwards compatible, but +V2 is recommended for new implementations. The CLI `jf rt permission-target-*` +commands use this API. + +| Component | Description | +|-----------|-------------| +| `repositories` | List of repo keys or patterns | +| `actions.users` | Map of username → list of actions | +| `actions.groups` | Map of group name → list of actions | + +Actions use lowercase: `read`, `write`, `annotate`, `delete`, `manage`. + +Does **not** support `destination` or `pipeline_source` resource types. + +API: `PUT /artifactory/api/security/permissions/{permissionName}`. + +### Key differences + +| Aspect | V1 (Permission Targets) | V2 (Access Permissions) | +|--------|------------------------|------------------------| +| Managed by | Artifactory | Access service | +| API base | `/artifactory/api/security/permissions/` | `/access/api/v2/permissions/` | +| Actions | lowercase (`read`, `write`) | uppercase (`READ`, `WRITE`) | +| Resource types | repos, builds, release bundles | + destinations, pipeline sources | +| Pattern fields | `includes_pattern` / `excludes_pattern` | `include_patterns` / `exclude_patterns` | +| CLI support | `jf rt permission-target-*` | No direct CLI commands (use REST) | + +For project-scoped RBAC, see Project roles in `platform-access-entities.md`. + +## Replication + +Replication synchronizes artifacts and properties between repositories, either +within the same instance or across Platform Deployments. + +| Type | Direction | Trigger | +|------|-----------|---------| +| **Push** | Source pushes to target | Scheduled or event-based | +| **Pull** | Target pulls from source | Scheduled | + +Replication configs are JSON templates applied per repository. Both artifact +content and properties are replicated. For federated repos, replication is +automatic and bi-directional across all member nodes. diff --git a/plugin/skills/jfrog/references/artifactory-operations.md b/plugin/skills/jfrog/references/artifactory-operations.md new file mode 100644 index 0000000..a244817 --- /dev/null +++ b/plugin/skills/jfrog/references/artifactory-operations.md @@ -0,0 +1,172 @@ +# Artifactory Operations + +CLI commands for managing Artifactory resources. All commands use the `jf rt` +namespace. Run `jf rt --help` to discover subcommands not listed here. + +## Repository management + +Repositories are created from JSON templates. The workflow is: + +1. Get a template: retrieve an existing repo config via + `jf api /artifactory/api/repositories/` + and modify it, or craft JSON manually. + Note: `jf rt repo-template` is interactive and cannot be used by agents. +2. Create: `jf rt repo-create ` +3. Update: `jf rt repo-update ` +4. Delete: `jf rt repo-delete --quiet` + +To list repositories, use: +`jf api /artifactory/api/repositories` + +## File operations + +- Upload: `jf rt upload ` +- Download: `jf rt download [target]` +- Search: `jf rt search ` +- Move: `jf rt move ` +- Copy: `jf rt copy ` +- Delete: `jf rt delete ` +- Set properties: `jf rt set-props "key=value"` +- Delete properties: `jf rt delete-props "key"` + +### Searching across repositories + +`jf rt search` expects a `/` argument. When the repo is unknown, +agents tend to use a leading wildcard (`jf rt search "*/path/..."`), which +generates an unscoped AQL internally and can time out on large instances. + +Use a direct AQL query with `name` and `path` criteria instead — omitting the +`repo` field searches all accessible repos via indexed columns: + +```bash +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" \ + -d 'items.find({ + "name":"", + "path":"" + }).include("repo","path","name","size","sha256")' +``` + +Add `"repo":""` to the criteria when the target repo is known, to +narrow the search further. + +## Build info + +### Publishing builds + +- Collect env: `jf rt build-collect-env ` +- Add git info: `jf rt build-add-git ` +- Publish: `jf rt build-publish ` +- Promote: `jf rt build-promote ` +- Discard: `jf rt build-discard ` + +### Retrieving build info + +The build detail API (`GET /api/build/{name}/{number}`) returns 404 when the +build is stored in a non-default build-info repo or belongs to a JFrog +Project. **Always resolve the scope before calling the build API:** + +1. If the user provided a project key or build-info repo, use it directly. +2. If you need to **list** build names or run numbers and you have a **project + key**, follow [Listing builds when the project key is known](#listing-builds-when-the-project-key-is-known) (REST first — do not jump to AQL). +3. If the project key and build-info repo are still unknown, discover scope + via AQL (see [Discovering build scope without a project key](#discovering-build-scope-without-a-project-key) below). +4. For **detail**, use a scoped detail GET — never call `GET /api/build//` without `?project=` or `?buildRepo=` when the build requires it. + +```bash +jf api "/artifactory/api/build//?buildRepo=" +jf api "/artifactory/api/build//?project=" +``` + +Scope parameters: + +- `?buildRepo=` — when the build info is stored in a + non-default build-info repository (anything other than + `artifactory-build-info`) +- `?project=` — when the build belongs to a JFrog Project + +### Listing builds when the project key is known + +When you have a **project key**, use this REST sequence before AQL. It scopes +the server’s work and avoids **unscoped** listing pitfalls (see below). + +1. **Build names** (one row per logical build): + `GET /api/build?project=` + Response includes `builds[]` with `uri` (path suffix per name) and + `lastStarted` (latest run for that name). + +2. **Run numbers for one name**: + `GET /api/build/?project=` + Response uses the field **`buildsNumbers`** (exact spelling from the API); + each entry has `uri` (e.g. `/33`) and `started`. The same number may appear + more than once with different `started` values — do not assume uniqueness + by number alone. + +3. **Full build info** (unchanged): + `GET /api/build//?project=` + +```bash +jf api "/artifactory/api/build?project=" +jf api "/artifactory/api/build/?project=" +jf api "/artifactory/api/build//?project=" +``` + +### Discovering build scope without a project key + +When the user has not provided the project key or build-info repo, discover +it via AQL. **Do not** use **unscoped** `GET /artifactory/api/build` (no +`?project=` or `?buildRepo=`) to list all builds — it can time out on large +instances with thousands of builds. + +Use AQL `builds.find()` instead. The builds domain **requires** `name`, +`number`, and `repo` in `.include()` for permission reasons — omitting `repo` +produces an error. + +```bash +jf api /artifactory/api/search/aql \ + -X POST -H "Content-Type: text/plain" \ + -d 'builds.find({"name":""}).include("name","number","repo").sort({"$desc":["number"]}).limit(10)' +``` + +The `build.repo` field in the response tells you which build-info repository +the build resides in. Use that value as the `buildRepo` parameter in the +detail GET. + +### Repository listing vs build-info + +`GET /artifactory/api/repositories?project=&type=buildinfo` may return +an empty list even when project-scoped build info exists (for example under +a `*-build-info` repository). Prefer the **build** endpoints above or AQL to +discover builds; do not treat an empty repository list as proof that no +builds exist. + +## Permissions + +Permission targets use JSON templates. +Note: `jf rt permission-target-template` is interactive. + +- Create: `jf rt permission-target-create ` +- Update: `jf rt permission-target-update ` +- Delete: `jf rt permission-target-delete ` + +## Users and groups + +- Create users: `jf rt users-create --csv ` +- Create single user: `jf rt user-create` (check `--help` for options) +- Delete users: `jf rt users-delete ` +- Create group: `jf rt group-create ` +- Delete group: `jf rt group-delete ` +- Add users to group: `jf rt group-add-users ` + +To get user details or update users, use `jf api`: +``` +jf api /access/api/v2/users/ +``` + +## Replication + +Replication configs use JSON templates. +Note: `jf rt replication-template` is interactive. + +- Create: `jf rt replication-create ` +- Delete: `jf rt replication-delete ` diff --git a/plugin/skills/jfrog/references/catalog-entities.md b/plugin/skills/jfrog/references/catalog-entities.md new file mode 100644 index 0000000..085bea0 --- /dev/null +++ b/plugin/skills/jfrog/references/catalog-entities.md @@ -0,0 +1,219 @@ +# Catalog entities + +When to read this file: + +- Querying **public package metadata** (descriptions, vulnerabilities, licenses, operational info). +- Working with the **Custom Catalog** (org-specific labels, package views, federation). +- Looking up **vulnerability details** beyond what Xray provides (advisories, EPSS, CWE, known exploits). +- Querying **OpenSSF scorecards**, **ML model metadata**, or **MCP service** registries. +- Using the OneModel GraphQL API with `publicPackages`, `customPackages`, + `publicSecurityInfo`, `publicLegalInfo`, `publicOperationalInfo`, + `publicCatalogLabels`, or `publicRemoteServices` query roots. + +Catalog entities are accessed via the **OneModel GraphQL API** +(`/onemodel/api/v1/graphql`). + +For the OneModel query workflow (credentials, schema fetch, validation, +execution), read `references/onemodel-graphql.md`. + +## Two catalog layers + +| Layer | Scope | Description | +|-------|-------|-------------| +| **Public Catalog** | Global | JFrog's curated package database — security, legal, and operational metadata for public packages across ecosystems | +| **Custom Catalog** | Organization | Org-specific overlay — custom labels, per-org package views, federation config | + +The Custom Catalog builds on top of the Public Catalog. A public package +can be enriched with org-specific labels and metadata through the Custom +Catalog without altering the underlying public data. + +## Public Catalog entities + +### PublicPackage + +A package as known to JFrog's global package database. + +| Field | Description | +|-------|-------------| +| `name` | Package name (e.g. `lodash`, `spring-boot-starter-web`) | +| `type` | Package type (e.g. `npm`, `maven`, `pypi`) | +| `ecosystem` | Ecosystem identifier | +| `description` | Rich-text description | +| `homepage`, `vcsUrl` | Package URLs | +| `vendor` | Maintainer or organization | +| `latestVersion` | Most recent version | +| `trendingScore` | Popularity score | +| `publishedAt`, `modifiedAt` | Timestamps | +| `mlModel` | ML model metadata (for HuggingFace etc.) | + +Connections: `versionsConnection`, `publicLabelsConnection`, `legalInfo`, +`operationalInfo`, `securityInfo`. + +Query: `publicPackages.searchPackages(where: {...})`. + +### PublicPackageVersion + +A specific version with security, legal, and operational analysis. + +| Field | Description | +|-------|-------------| +| `version` | Version string | +| `isLatest` | Whether this is the latest version | +| `isListedVersion` | Whether visible in Catalog UI | +| `publishedAt`, `modifiedAt` | Timestamps | +| `trendingScore` | Version-level popularity | +| `dependencies` | Dependency information | +| `mlModelMetadata`, `mlInfo` | ML/AI-related metadata | + +Each version carries three info blocks: +- `securityInfo` — vulnerability data, maliciousness, contextual analysis +- `legalInfo` — licenses, copyrights +- `operationalInfo` — end-of-life, OpenSSF scores, popularity metrics + +### PublicVulnerability + +Vulnerability data richer than what Xray violations expose. Useful for +deep-dive security analysis and advisory lookups. + +| Field | Description | +|-------|-------------| +| `name` | CVE identifier (e.g. `CVE-2021-44228`) | +| `ecosystem` | Affected ecosystem | +| `severity` | `CRITICAL`, `HIGH`, `MEDIUM`, `LOW` | +| `description` | Detailed impact description | +| `cvss` | CVSS scores — v2, v3, **and v4** | +| `epss` | EPSS (Exploit Prediction Scoring System) — exploit likelihood | +| `knownExploit` | Known exploit information | +| `withdrawn` | Whether the CVE has been retracted | +| `aliases` | Alternative identifiers | +| `references` | Advisory URLs | +| `publishedAt`, `modifiedAt` | Timestamps | + +Advisory sources (via `advisories` connection): +- **NVD** — NIST National Vulnerability Database +- **GHSA** — GitHub Security Advisory +- **JFrog Advisory** — JFrog's own research (includes impact reasons) +- **Debian Security Tracker** +- **RedHat OVAL** + +Additional connections: `cwesConnection` (CWE entries), `cpesConnection` +(CPE entries), `publicPackageInfo` (affected packages and versions). + +Query: `publicSecurityInfo.searchVulnerabilities(where: {...})`. + +#### Filtering limitations + +`searchVulnerabilities` can filter by CVE name, ecosystem, severity, CVSS, +EPSS, known exploit status, and publication date — but **not** by affected +package name. There is no `hasPublicPackageInfoWith` or similar filter on +`PublicVulnerabilityWhereInput`. To find vulnerabilities affecting a specific +package, use one of these alternatives: + +- **Version-level security info** (GraphQL): query + `publicPackages.getPackage(type, name)` and navigate to + `versionsConnection → securityInfo → vulnerabilitiesConnection` to get + CVEs affecting specific versions. +- **Individual CVE lookup**: use `searchVulnerabilities(where: { name: "" })` + and inspect `publicPackageInfo.vulnerablePublicPackagesConnection` on the + `generic` ecosystem entry. + +#### Ecosystem multiplicity + +A single CVE appears as multiple `PublicVulnerability` entries — one per +ecosystem. The `ecosystem` field determines which entry you see: + +| Ecosystem | Contains | +|-----------|----------| +| `generic` | Non-OS package-level data (npm, maven, pypi, go, etc.) — includes `publicPackageInfo` with vulnerable versions and fix versions | +| `debian`, `redhat`, `ubuntu`, etc. | OS-specific advisory data — severity may differ from NVD; `publicPackageInfo` is typically empty (OS packages are tracked separately) | + +When looking up a CVE by name, `searchVulnerabilities(where: { name: "" })` +returns all ecosystem entries. To get affected packages and fix versions for +libraries like npm or maven, filter for or focus on the `generic` ecosystem +entry. `getVulnerability` requires both `name` and `ecosystem` — use +`searchVulnerabilities` when the ecosystem is unknown. + +### PublicLicense + +License metadata with permission, condition, and limitation details. + +| Field | Description | +|-------|-------------| +| `name` | License name (e.g. `Apache-2.0`, `MIT`) | +| `spdxId` | SPDX identifier | +| `permissions` | What the license permits | +| `limitations` | Restrictions imposed | +| `patentConditions` | Patent grant conditions | +| `noticeFiles` | Required notices | + +Query: `publicLegalInfo.searchLicenses(where: {...})`. + +### PublicPackageOperationalInfo + +Operational risk assessment for packages and versions. + +| Entity | Key data | +|--------|----------| +| **OpenSSF scorecard** | Overall score, individual checks with scores and pass/fail | +| **End-of-life** | Whether the package or version is EOL, justification | +| **Popularity** | JFrog popularity by segment and subscription tier, download counts | + +### MCP services and tools + +The Public Catalog also indexes MCP (Model Context Protocol) services: + +| Entity | Description | +|--------|-------------| +| `PublicMcpService` | An MCP service with name, description, version | +| `PublicMcpTool` | A tool exposed by an MCP service with arguments | +| `PublicMcpRemote` | Remote MCP server configuration | + +Query: `publicRemoteServices.searchMcpServices(where: {...})`. + +## Custom Catalog entities + +### CustomPackage + +A package in the organization's private catalog view. + +| Field | Description | +|-------|-------------| +| `customCatalogId` | Org-scoped identifier | +| `name`, `type`, `ecosystem`, `namespace` | Package identity | +| `isListedPackage` | Whether visible in Catalog UI | +| `customCatalogAddedAt`, `customCatalogModifiedAt` | Org-specific timestamps | + +Connections: `versionsConnection`, `legalInfo`, +`customCatalogLabelsConnection`. + +### CustomCatalogLabel + +Organization-defined labels for categorizing packages. + +| Field | Description | +|-------|-------------| +| `name` | Label name | +| `description` | What the label represents | +| `color` | Display color | +| `labelType` | `MANUAL` or `AUTOMATIC` | +| `assignmentInfo` | How and when the label was assigned | + +Labels can be assigned to both custom packages and public packages/versions +within the org's catalog scope. The Custom Catalog mutations allow +creating, updating, and deleting labels. + +### CustomCatalogFederation + +Configuration for federating catalog data across JFrog deployments. + +## Catalog vs. Xray vs. Stored Packages + +These three domains provide different views of package and security data: + +| Aspect | Catalog | Xray | Stored Packages | +|--------|---------|------|-----------------| +| **Scope** | Global knowledge base + org overlay | Instance-scoped scanning | Instance-scoped storage | +| **Security** | CVE advisories, EPSS, CVSS v2/v3/v4, known exploits | Watches, policies, violations | Vulnerability summary (deprecated) | +| **Packages** | Public metadata (description, homepage, OpenSSF) | Components identified during scanning | Packages/versions stored in Artifactory | +| **Access** | GraphQL only | REST + CLI (`jf api /xray/...`) | GraphQL only | +| **Use case** | Research, compliance reporting, package evaluation | Runtime enforcement, CI/CD gating | Inventory, location queries | diff --git a/plugin/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md b/plugin/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md new file mode 100644 index 0000000..cdebf69 --- /dev/null +++ b/plugin/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md @@ -0,0 +1,103 @@ +# Bulk operations and agent execution patterns + +Platform-wide guidance for agents that gather data from multiple JFrog products +(Artifactory, Xray, Access, Distribution, etc.), run long shell +sequences, or parallelize work. Product-specific field names and endpoints live +in the other `references/*` files; this document describes **patterns**, not +one workflow. + +## List vs detail responses + +Many REST surfaces expose a **light list** (keys, names, minimal fields) and a +**richer GET by id or key**. Fields needed for audits, reporting, joins, or +permission checks may appear **only** on the detail response. Before building a +multi-step flow on a single list call, confirm in API docs or with a sample GET +whether the fields you need are present. + +## Volume, batching, and timeouts + +- Estimate **N** round-trips (list + per-item GETs, paginated APIs, etc.) before + starting so execution time and tool timeouts stay predictable. +- Prefer batching independent reads in one Shell invocation when credentials and + tier match (see SKILL.md **Batch and parallel execution**). +- Split very large work across chunks, parallel Shell calls, or subagents when + the skill's tiering guidance says so. +- Before starting an N+1 loop (list + per-item detail), **estimate wall time** + as roughly `N * 1.5s` for sequential calls. Set `block_until_ms` to at + least that estimate plus a 30-second buffer. +- For loops exceeding ~60 items, prefer a single Shell invocation that writes + progress to a log file (`>> /tmp/jf-progress-$$.log`) so partial results + are visible even if the job is interrupted. +- If the task is read-only and items are independent, consider Tier 2 or + Tier 3 parallelism (see `general-parallel-execution.md`) to reduce total time — + but respect rate limits and keep concurrency modest (4-8 parallel calls). + +## Parallelism and shared files + +**Unsafe:** Multiple concurrent processes appending lines to the **same** file +(JSONL, logs, ndjson) without synchronization. Output can interleave on one +line and break parsers (e.g. JSON "Extra data" errors). + +**Safer:** + +- Write sequentially to one file; or +- One temp file per worker or chunk, then concatenate; or +- Use advisory locking (`flock`) if one file must be shared. + +## Agent sandboxes and the environment check + +`scripts/check-environment.sh` does **not** call your JFrog server, but it may +make an outbound request to `releases.jfrog.io` for version checking and may +**write** +`/local-cache/jfrog-skill-state.json` when the cache is stale or missing. In a +restricted agent sandbox, **workspace write** access can fail even when +`full_network` is granted. Request permissions that allow writing `/local-cache` +when the check fails with a filesystem error. + +For bulk API or CLI output files, use `/tmp` or `mktemp`; do not use +`local-cache/` except for `jfrog-skill-state.json` and the OneModel schema file +(see main SKILL.md). + +## Shell hygiene + +- Use `set -euo pipefail` in non-trivial scripts so failures are not silent. +- Use unique temp paths (e.g. `$$` in the filename) and **echo the expanded + path** so it can be reused across Shell calls (see SKILL.md **Preserving + command output** for the `$$` + echo, session ID, and hardcoded patterns). +- Parse CLI and API JSON with **`jq`**. + +## Safe multi-response collection + +When looping over items (repos, builds, users) and fetching detail for each: + +1. Save each response to a variable or per-item file. +2. Validate with `jq -e . >/dev/null 2>&1` before appending. +3. On validation failure, write a structured error line so the caller can + report partial results instead of crashing. +4. After the loop, `jq -s '.' results.ndjson` to produce a single array. + +```bash +: >results.ndjson +while read -r key; do + body=$(jf api "/artifactory/api/repositories/$key" || true) + if echo "$body" | jq -e . >/dev/null 2>&1; then + echo "$body" | jq -c . >>results.ndjson + else + printf '{"key":"%s","_error":"invalid_response"}\n' "$key" >>results.ndjson + fi +done < <(jq -r '.[].key' list.json) +jq -s '.' results.ndjson > details.json +``` + +Never pipe a loop of `jf api` calls directly into `jq -s` without +per-body validation. + +## Where to find product specifics + +- Artifactory REST nuances: `references/artifactory-api-gaps.md` +- Platform admin / Access: `references/platform-admin-api-gaps.md` +- JFrog Projects (endpoints): `references/projects-api.md` +- Joining Artifactory repos to Projects (`projectKey`, roles, environments): + `references/platform-access-entities.md` +- Platform API invocation (all products through `jf api`): see + `SKILL.md` § *Invoking platform APIs with `jf api`* diff --git a/plugin/skills/jfrog/references/general-parallel-execution.md b/plugin/skills/jfrog/references/general-parallel-execution.md new file mode 100644 index 0000000..d7354e9 --- /dev/null +++ b/plugin/skills/jfrog/references/general-parallel-execution.md @@ -0,0 +1,133 @@ +# Batch and Parallel Execution + +When a task requires multiple independent operations, use the lightest +parallelism mechanism that fits. Three tiers are available, from lightest to +heaviest: + +| Tier | Mechanism | Best for | +|------|-----------|----------| +| 1 | Single Shell call with `&&` | Few commands, same credentials | +| 2 | Parallel Shell tool calls | Independent commands that can run concurrently | +| 3 | Parallel subagents (Task tool) | Large multi-step jobs where each branch needs its own reasoning | + +## Tier 1: Batch within a single Shell call + +Combine independent commands with `&&`. All JFrog API calls go through the +same `jf api` command and the same `jf config` server, so batching them +together is both safe and efficient: + +```bash +jf api /artifactory/api/repositories > /tmp/jf-repos-$$.json && \ +jf api /artifactory/api/system/ping > /tmp/jf-ping-$$.json && \ +jf api /artifactory/api/storageinfo > /tmp/jf-storage-$$.json +``` + +Cross-product reads batch the same way: + +```bash +jf api /access/api/v2/users/ > /tmp/jf-users-$$.json && \ +jf api /access/api/v2/groups/ > /tmp/jf-groups-$$.json && \ +jf api /access/api/v2/permissions/ > /tmp/jf-perms-$$.json +``` + +## Tier 2: Parallel Shell tool calls + +Use multiple Shell tool calls in the same message when the commands are +independent and the total runtime benefits from concurrency: + +```bash +# Shell call 1 — echo the expanded path so the agent can reference it later +OUT=/tmp/jf-repos-$$.json +jf api /artifactory/api/repositories > "$OUT" && echo "$OUT" + +# Shell call 2 (parallel) — same pattern, different PID +OUT=/tmp/jf-users-$$.json +jf api /access/api/v2/users/ > "$OUT" && echo "$OUT" +``` + +Each parallel Shell call gets a different PID, so `$$` expands to different +values. Echo the path so the agent knows the literal filename for cross-call +use (see SKILL.md **Preserving command output**). + +## Tier 3: Parallel subagents + +For tasks with multiple independent branches that each require several steps +or their own reasoning — such as generating a platform health report with +separate sections, auditing both repository config and security policies, or +comparing configurations across servers the user explicitly named — launch +parallel subagents using the Task tool. + +Each subagent runs autonomously, executes its own CLI/API calls, and returns +a structured result. The parent agent assembles the final answer. + +### Example — platform audit with three parallel subagents + +``` +Subagent 1 (shell): "Collect repository data" + → jf api /artifactory/api/repositories + → jf api /artifactory/api/storageinfo + → Return repo count, types, total size + +Subagent 2 (shell): "Collect security configuration" + → jf api /xray/api/v2/policies + → jf api /xray/api/v2/watches + → Return policy count, watch count, coverage gaps + +Subagent 3 (shell): "Collect user and permission data" + → jf api /access/api/v2/users/ + → jf api /access/api/v2/groups/ + → jf api /access/api/v2/permissions/ + → Return user count, group count, admin users +``` + +All three subagents run concurrently. Once all complete, the parent agent +merges their results into a unified report. + +### How to structure a subagent prompt + +1. State the goal clearly (e.g. "Collect all Xray policies and watches"). +2. Provide the exact commands to run, or name the API tier and let the + subagent discover via `--help`. +3. Tell the subagent to save output to `/tmp/jf-