@jfrog/opencode-jfrog-plugin 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +105 -51
  2. package/dist/index.js +30 -240
  3. package/package.json +6 -6
  4. package/skills/jfrog/SKILL.md +529 -0
  5. package/skills/jfrog/assets/.gitkeep +0 -0
  6. package/skills/jfrog/references/apptrust-entities.md +154 -0
  7. package/skills/jfrog/references/artifactory-api-gaps.md +206 -0
  8. package/skills/jfrog/references/artifactory-aql-syntax.md +656 -0
  9. package/skills/jfrog/references/artifactory-entities.md +236 -0
  10. package/skills/jfrog/references/artifactory-operations.md +178 -0
  11. package/skills/jfrog/references/catalog-entities.md +219 -0
  12. package/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md +93 -0
  13. package/skills/jfrog/references/general-parallel-execution.md +131 -0
  14. package/skills/jfrog/references/general-use-case-hints.md +27 -0
  15. package/skills/jfrog/references/jfrog-brand-html-report.md +98 -0
  16. package/skills/jfrog/references/jfrog-cli-install-upgrade.md +30 -0
  17. package/skills/jfrog/references/jfrog-entity-index.md +112 -0
  18. package/skills/jfrog/references/jfrog-login-flow.md +132 -0
  19. package/skills/jfrog/references/jfrog-url-references.md +51 -0
  20. package/skills/jfrog/references/onemodel-common-patterns.md +323 -0
  21. package/skills/jfrog/references/onemodel-graphql.md +446 -0
  22. package/skills/jfrog/references/onemodel-query-examples.md +753 -0
  23. package/skills/jfrog/references/platform-access-entities.md +200 -0
  24. package/skills/jfrog/references/platform-admin-api-gaps.md +164 -0
  25. package/skills/jfrog/references/platform-admin-operations.md +58 -0
  26. package/skills/jfrog/references/projects-api.md +241 -0
  27. package/skills/jfrog/references/release-lifecycle-entities.md +180 -0
  28. package/skills/jfrog/references/stored-packages-entities.md +165 -0
  29. package/skills/jfrog/references/xray-entities.md +740 -0
  30. package/skills/jfrog/scripts/check-environment.sh +224 -0
  31. package/skills/jfrog/scripts/jfrog-login-register-session.sh +84 -0
  32. package/skills/jfrog/scripts/jfrog-login-save-credentials.sh +128 -0
  33. package/skills/jfrog-package-safety-and-download/SKILL.md +275 -0
  34. package/sync-skills-vendor.json +5 -0
@@ -0,0 +1,656 @@
1
+ # AQL (Artifactory Query Language)
2
+
3
+ AQL queries are sent as POST requests with `Content-Type: text/plain`:
4
+
5
+ ```bash
6
+ jf api /artifactory/api/search/aql \
7
+ -X POST -H "Content-Type: text/plain" -d '<query>'
8
+ ```
9
+
10
+ ## Query structure
11
+
12
+ ```
13
+ <domain>.find(<criteria>)
14
+ .include(<fields>)
15
+ .sort(<sort>)
16
+ .offset(<n>)
17
+ .limit(<n>)
18
+ .distinct(<boolean>)
19
+ ```
20
+
21
+ Only `.find()` is required. The others are optional and chainable.
22
+ **The chain order above is enforced by the server.** `.include()` must come
23
+ before `.sort()`, `.sort()` before `.offset()`, etc. Putting them out of
24
+ order (e.g. `.sort()` before `.include()`) produces a parse error.
25
+
26
+ **Mandatory include fields:** `items` requires `"repo","path","name"`;
27
+ `builds` requires `"name","number","repo"`. Always include these even when
28
+ you only need a subset — narrow results with `jq` post-query instead:
29
+
30
+ ```
31
+ items.find({"name":"commons-lang3-3.12.0.jar"})
32
+ .include("repo","path","name")
33
+ .distinct(true)
34
+ ```
35
+
36
+ ## Domains
37
+
38
+ AQL has 13 queryable domains. Each domain represents a different entity type
39
+ and has its own set of fields.
40
+
41
+
42
+ | Domain | Query name | Description |
43
+ | -------------------- | ------------------- | ---------------------------------------------- |
44
+ | Items | `items` | Artifacts stored in repositories (most common) |
45
+ | Properties | `properties` | Key-value properties on items |
46
+ | Item infos | `item.infos` | Property modification metadata |
47
+ | Statistics | `stats` | Download statistics (local and remote) |
48
+ | Builds | `builds` | Build info records |
49
+ | Build modules | `modules` | Modules within a build |
50
+ | Build artifacts | `artifacts` | Artifacts produced by a build module |
51
+ | Build dependencies | `dependencies` | Dependencies consumed by a build module |
52
+ | Build properties | `build.properties` | Key-value properties on builds |
53
+ | Build promotions | `build.promotions` | Build promotion records |
54
+ | Module properties | `module.properties` | Key-value properties on build modules |
55
+ | Release bundles | `releases` | Release bundle records |
56
+ | Release bundle files | `release_artifacts` | Files within a release bundle |
57
+
58
+
59
+ ## Domain relationships
60
+
61
+ Domains connect through the following join paths. Cross-domain queries
62
+ traverse these links — fields from related domains can appear in criteria
63
+ and include clauses by prefixing the domain path.
64
+
65
+ ```mermaid
66
+ erDiagram
67
+ items ||--o{ properties : "has"
68
+ items ||--o| item_infos : "has"
69
+ items ||--o{ stats : "has"
70
+ items ||--o{ artifacts : "via checksum"
71
+ items ||--o{ dependencies : "via checksum"
72
+ items ||--o{ release_artifacts : "has"
73
+ artifacts }o--|| modules : "belongs to"
74
+ dependencies }o--|| modules : "belongs to"
75
+ modules }o--|| builds : "belongs to"
76
+ modules ||--o{ module_properties : "has"
77
+ builds ||--o{ build_properties : "has"
78
+ builds ||--o{ build_promotions : "has"
79
+ release_artifacts }o--|| releases : "belongs to"
80
+ ```
81
+
82
+
83
+
84
+ **Key:** Items connect to build artifacts and dependencies through SHA-1
85
+ checksum matching, not a direct key. This means a cross-domain query from
86
+ items to builds traverses: items → artifacts → modules → builds.
87
+
88
+ ### Cross-domain field paths
89
+
90
+ To reference a field from a related domain, use dot-separated domain paths:
91
+
92
+ ```
93
+ items.find({"artifact.module.build.name":"my-build"})
94
+ .include("name","repo","path","artifact.module.build.number")
95
+ ```
96
+
97
+ Common cross-domain paths from items:
98
+
99
+ - `stat.downloads`, `stat.downloaded` — download statistics
100
+ - `property.key`, `property.value` — item properties
101
+ - `artifact.module.build.name` — build that produced the item
102
+ - `artifact.module.build.number` — build number
103
+
104
+ From builds:
105
+
106
+ - `module.artifact.name` — artifacts in build modules
107
+ - `module.dependency.name` — dependencies of build modules
108
+
109
+ ## Fields by domain
110
+
111
+ Field types: `string`, `date`, `int`, `long`, `itemType` (`file`, `folder`,
112
+ or `any`). Fields marked "default" are returned without explicit `.include()`.
113
+
114
+ ### items
115
+
116
+
117
+ | Field | Type | Default |
118
+ | --------------- | -------- | ------- |
119
+ | `repo` | string | yes |
120
+ | `path` | string | yes |
121
+ | `name` | string | yes |
122
+ | `type` | itemType | yes |
123
+ | `size` | long | yes |
124
+ | `depth` | int | yes |
125
+ | `created` | date | yes |
126
+ | `created_by` | string | yes |
127
+ | `modified` | date | yes |
128
+ | `modified_by` | string | yes |
129
+ | `updated` | date | yes |
130
+ | `actual_md5` | string | no |
131
+ | `actual_sha1` | string | no |
132
+ | `sha256` | string | no |
133
+ | `original_md5` | string | no |
134
+ | `original_sha1` | string | no |
135
+
136
+
137
+ Computed field: `virtual_repos` — returns virtual repositories that include
138
+ the item's actual repository. Must use `.include("virtual_repos")` explicitly;
139
+ requires `repo`, `path`, `name` in the result set.
140
+
141
+ ### properties
142
+
143
+
144
+ | Field | Type | Default |
145
+ | ------- | ------ | ------- |
146
+ | `key` | string | yes |
147
+ | `value` | string | yes |
148
+
149
+
150
+ ### stats
151
+
152
+
153
+ | Field | Type | Default |
154
+ | ---------------------- | ------ | ------- |
155
+ | `downloads` | int | yes |
156
+ | `downloaded` | date | yes |
157
+ | `downloaded_by` | string | yes |
158
+ | `remote_downloads` | int | yes |
159
+ | `remote_downloaded` | date | yes |
160
+ | `remote_downloaded_by` | string | yes |
161
+ | `remote_origin` | string | yes |
162
+ | `remote_path` | string | yes |
163
+
164
+
165
+ ### item.infos
166
+
167
+
168
+ | Field | Type | Default |
169
+ | ------------------- | ------ | ------- |
170
+ | `props_modified` | date | yes |
171
+ | `props_modified_by` | string | yes |
172
+ | `props_md5` | string | yes |
173
+
174
+
175
+ ### builds
176
+
177
+
178
+ | Field | Type | Default |
179
+ | ------------- | ------ | ------- |
180
+ | `url` | string | yes |
181
+ | `name` | string | yes |
182
+ | `number` | string | yes |
183
+ | `started` | date | yes |
184
+ | `created` | date | yes |
185
+ | `created_by` | string | yes |
186
+ | `modified` | date | yes |
187
+ | `modified_by` | string | yes |
188
+ | `repo` | string | no |
189
+
190
+
191
+ ### modules
192
+
193
+
194
+ | Field | Type | Default |
195
+ | ------ | ------ | ------- |
196
+ | `name` | string | yes |
197
+
198
+
199
+ ### artifacts
200
+
201
+
202
+ | Field | Type | Default |
203
+ | ------ | ------ | ------- |
204
+ | `name` | string | yes |
205
+ | `type` | string | yes |
206
+ | `sha1` | string | yes |
207
+ | `md5` | string | yes |
208
+
209
+
210
+ ### dependencies
211
+
212
+
213
+ | Field | Type | Default |
214
+ | ------- | ------ | ------- |
215
+ | `name` | string | yes |
216
+ | `scope` | string | yes |
217
+ | `type` | string | yes |
218
+ | `sha1` | string | yes |
219
+ | `md5` | string | yes |
220
+
221
+
222
+ ### build.properties
223
+
224
+
225
+ | Field | Type | Default |
226
+ | ------- | ------ | ------- |
227
+ | `key` | string | yes |
228
+ | `value` | string | yes |
229
+
230
+
231
+ ### build.promotions
232
+
233
+
234
+ | Field | Type | Default |
235
+ | ------------ | ------ | ------- |
236
+ | `created` | date | yes |
237
+ | `created_by` | string | yes |
238
+ | `status` | string | yes |
239
+ | `repo` | string | yes |
240
+ | `comment` | string | yes |
241
+ | `user` | string | yes |
242
+
243
+
244
+ ### module.properties
245
+
246
+
247
+ | Field | Type | Default |
248
+ | ------- | ------ | ------- |
249
+ | `key` | string | yes |
250
+ | `value` | string | yes |
251
+
252
+
253
+ ### releases
254
+
255
+
256
+ | Field | Type | Default |
257
+ | -------------- | --------------------------- | ------- |
258
+ | `name` | string | yes |
259
+ | `version` | string | yes |
260
+ | `status` | string | yes |
261
+ | `created` | date | yes |
262
+ | `signature` | string | yes |
263
+ | `type` | string (`SOURCE`, `TARGET`) | yes |
264
+ | `storing_repo` | string | yes |
265
+
266
+
267
+ ### release_artifacts
268
+
269
+
270
+ | Field | Type | Default |
271
+ | ------ | ------ | ------- |
272
+ | `path` | string | yes |
273
+
274
+
275
+ ## Comparators
276
+
277
+
278
+ | Operator | Meaning | Example |
279
+ | ---------- | -------------------------------- | ------------------------------------ |
280
+ | `$eq` | Equals (default if omitted) | `{"type":"file"}` |
281
+ | `$ne` | Not equals | `{"type":{"$ne":"folder"}}` |
282
+ | `$eqic` | Equals, case-insensitive | `{"name":{"$eqic":"README.md"}}` |
283
+ | `$match` | Wildcard match (`*`, `?`) | `{"name":{"$match":"*.jar"}}` |
284
+ | `$matchic` | Wildcard match, case-insensitive | `{"name":{"$matchic":"*.JAR"}}` |
285
+ | `$nmatch` | Wildcard not-match | `{"name":{"$nmatch":"*-SNAPSHOT*"}}` |
286
+ | `$gt` | Greater than | `{"size":{"$gt":"1000000"}}` |
287
+ | `$gte` | Greater than or equal | `{"stat.downloads":{"$gte":"10"}}` |
288
+ | `$lt` | Less than | `{"size":{"$lt":"5000"}}` |
289
+ | `$lte` | Less than or equal | `{"modified":{"$lte":"2025-01-01"}}` |
290
+
291
+
292
+ ### Boolean operators
293
+
294
+
295
+ | Operator | Description |
296
+ | -------- | ---------------------------------------------------------------------- |
297
+ | `$and` | All conditions must match (implicit when fields are at the same level) |
298
+ | `$or` | Any condition must match |
299
+
300
+
301
+ ```
302
+ items.find({"$and":[
303
+ {"repo":"my-repo"},
304
+ {"$or":[
305
+ {"name":{"$match":"*.jar"}},
306
+ {"name":{"$match":"*.war"}}
307
+ ]}
308
+ ]})
309
+ ```
310
+
311
+ ### Relative date comparators
312
+
313
+ AQL supports relative date queries with `$last` and `$before`:
314
+
315
+
316
+ | Operator | Meaning | Example |
317
+ | --------- | ------------------------------------------------------- | ------------------------------- |
318
+ | `$last` | Within the last N period (equivalent to `$gt` from now) | `{"modified":{"$last":"7d"}}` |
319
+ | `$before` | Before the last N period (equivalent to `$lt` from now) | `{"created":{"$before":"3mo"}}` |
320
+
321
+
322
+ Supported units: `d` (days), `w` (weeks), `mo` (months), `y` (years),
323
+ `s` (seconds), `mi` (minutes), `ms` (milliseconds).
324
+
325
+ ### Multi-property AND
326
+
327
+ To match items that have property A=1 **and** property B=2 (different
328
+ property rows), use `$and` with `@` shorthand:
329
+
330
+ ```
331
+ items.find({"$and":[
332
+ {"@build.name":"my-build"},
333
+ {"@build.number":"42"}
334
+ ]})
335
+ ```
336
+
337
+ AQL also documents a `$msp` (multi-set property) operator for this purpose,
338
+ but `$msp` is **unreliable in practice** — it returns 0 results on many
339
+ server versions even when matching items exist. Prefer `$and` with `@`
340
+ shorthand, which is verified to work correctly.
341
+
342
+ ## Date queries
343
+
344
+ Dates use ISO 8601 format for absolute dates:
345
+
346
+ ```
347
+ items.find({"modified":{"$gt":"2025-06-01T00:00:00.000Z"}})
348
+ ```
349
+
350
+ Or use relative dates (preferred — avoids hardcoding timestamps):
351
+
352
+ ```
353
+ items.find({"modified":{"$last":"30d"}})
354
+ items.find({"created":{"$before":"6mo"}})
355
+ ```
356
+
357
+ ## Property queries
358
+
359
+ Two equivalent syntaxes for property filtering:
360
+
361
+ **`@key` shorthand** — concise, works for single property conditions:
362
+
363
+ ```
364
+ items.find({"repo":"my-repo","@build.name":"my-build","type":"file"})
365
+ ```
366
+
367
+ **Explicit form** — `property.key`/`property.value` pairs:
368
+
369
+ ```
370
+ items.find({
371
+ "repo":"my-repo",
372
+ "property.key":"build.name",
373
+ "property.value":"my-build"
374
+ })
375
+ ```
376
+
377
+ **Multi-property AND** — use `$and` with `@` shorthand to match across
378
+ different property rows:
379
+
380
+ ```
381
+ items.find({"$and":[
382
+ {"@build.name":"my-build"},
383
+ {"@build.number":"42"}
384
+ ]})
385
+ ```
386
+
387
+ > **Note:** The `@key` shorthand works inside `$and`. For `$or`, use the
388
+ > explicit `property.key`/`property.value` form if the shorthand does not
389
+ > return expected results.
390
+
391
+ ## Include
392
+
393
+ Select which fields to return. Without `.include()`, AQL returns each
394
+ domain's default field set.
395
+
396
+ **When you use `.include()`, you replace the defaults — so you must
397
+ explicitly list any required fields:**
398
+
399
+ - `items` domain: always include `"repo","path","name"` (server rejects
400
+ the query otherwise)
401
+ - `builds` domain: always include `"name","number","repo"`
402
+
403
+ ```
404
+ items.find({"repo":"my-repo"})
405
+ .include("name","repo","path","size","sha256","stat.downloads")
406
+ ```
407
+
408
+ Cross-domain includes use dot-separated paths:
409
+
410
+ ```
411
+ items.find({"repo":"my-repo"})
412
+ .include("name","repo","path","property.key","property.value")
413
+ ```
414
+
415
+ ## Sort and pagination
416
+
417
+ ```
418
+ items.find({"repo":"my-repo"})
419
+ .sort({"$desc":["modified"]})
420
+ .offset(0)
421
+ .limit(50)
422
+ ```
423
+
424
+ Sort directions: `$asc`, `$desc`. Sort fields must also appear in the result
425
+ set (explicit `.include()` or default fields). See
426
+ [Before constructing a query](#before-constructing-a-query) for sort
427
+ performance rules.
428
+
429
+ ## Distinct
430
+
431
+ Deduplicate result rows:
432
+
433
+ ```
434
+ items.find({"repo":"my-repo"}).distinct(true)
435
+ ```
436
+
437
+ ## Validation rules
438
+
439
+ The server enforces these constraints — violating them produces an error:
440
+
441
+ **Non-admin users:**
442
+
443
+ - `items` domain queries must include `repo`, `path`, `name` in results
444
+ (needed for permission filtering)
445
+ - `builds` domain queries must include `name`, `number`, `repo` in results
446
+
447
+ **Transitive mode** (`.transitive()` for querying through virtual repos):
448
+
449
+ - Only works with `items` domain
450
+ - Include subdomains limited to `items` and `properties`
451
+ - Repo criteria must use `$eq` (exact match) with a single repository
452
+ - No `offset` or `sort` allowed
453
+
454
+ ## Before constructing a query
455
+
456
+ Run through these checks before writing any AQL query:
457
+
458
+ 1. **Never `.sort()` without a `repo` filter** — forces a full table scan
459
+ across all repositories. Sort client-side with `jq` instead. Also,
460
+ `.sort()` on cross-domain fields (e.g. `stat.downloads` in `items.find()`)
461
+ is silently ignored — fetch all rows and sort client-side.
462
+ 2. **Always set `.limit()`** — no built-in default limit; unbounded queries
463
+ can time out or OOM. Broad queries without a `repo` filter are especially
464
+ expensive.
465
+ 3. **`range.total` = returned count, not total matching** — AQL has no
466
+ count-only mode. To find the true total, paginate with `.offset()` until
467
+ a page returns fewer results than the limit.
468
+ 4. **AQL has no repo-type field** — to restrict to local repos, either
469
+ pre-query `GET /api/repositories?type=local` and add repo names to
470
+ criteria (practical when count is small), or query without a repo filter
471
+ and exclude `-cache` / `-virtual` suffixed repos client-side with `jq`.
472
+ 5. **Narrow server-side first** — add every applicable filter (`created_by`,
473
+ `created`, `type`, `name`) before relying on client-side `jq` filtering.
474
+
475
+ ## Common query patterns
476
+
477
+ ### Find all JARs in a repo
478
+
479
+ ```
480
+ items.find({"repo":"libs-release","name":{"$match":"*.jar"}})
481
+ ```
482
+
483
+ ### Find large files (> 100 MB)
484
+
485
+ ```
486
+ items.find({"repo":"my-repo","size":{"$gt":"104857600"},"type":"file"})
487
+ ```
488
+
489
+ ### Find Maven SNAPSHOT JARs
490
+
491
+ Use `*-SNAPSHOT*.jar` (not `*-SNAPSHOT.jar`) to also match classifier
492
+ artifacts like `-sources.jar` and `-javadoc.jar`:
493
+
494
+ ```
495
+ items.find({"repo":"libs-snapshot","name":{"$match":"*-SNAPSHOT*.jar"},"type":"file"})
496
+ ```
497
+
498
+ ### Find artifacts modified in the last 7 days
499
+
500
+ ```
501
+ items.find({"repo":"my-repo","modified":{"$last":"7d"},"type":"file"})
502
+ .sort({"$desc":["modified"]})
503
+ .limit(100)
504
+ ```
505
+
506
+ ### Docker queries
507
+
508
+ Use `"name":"manifest.json"` to **list tags** (one per tag). Use
509
+ `"name":{"$match":"*manifest.json"}` to **query all manifests** (includes
510
+ `list.manifest.json` for multi-arch tags — see [Gotchas](#gotchas)).
511
+
512
+ ```
513
+ items.find({"repo":"docker-local","path":{"$match":"my-image/*"},"name":"manifest.json"})
514
+ ```
515
+
516
+ ### Docker image size
517
+
518
+ **Do not use AQL** — layer blobs live at `<image>/sha256:<digest>/`, not
519
+ under `<image>/<tag>/`. Use the V2 manifest API (returns `layers[].size`):
520
+
521
+ ```bash
522
+ jf api "/artifactory/api/docker/<repo>/v2/<image>/manifests/<tag>" \
523
+ -H "Accept: application/vnd.docker.distribution.manifest.v2+json"
524
+ ```
525
+
526
+ For multi-arch images the response is an image index; fetch each platform
527
+ manifest by digest to get its layers.
528
+
529
+ ### Find artifacts with a specific property
530
+
531
+ ```
532
+ items.find({"repo":"my-repo","@build.name":"my-build","type":"file"})
533
+ ```
534
+
535
+ ### Find never-downloaded files (zero download count)
536
+
537
+ Zero-download items lack a stats row — filter client-side instead
538
+ (see [Gotchas](#gotchas)):
539
+
540
+ ```bash
541
+ jf api /artifactory/api/search/aql \
542
+ -X POST -H "Content-Type: text/plain" -d '
543
+ items.find({"repo":"my-repo","type":"file"})
544
+ .include("repo","path","name","size","stat.downloads")
545
+ ' | jq '[.results[] | select((.stats[0].downloads // 0) == 0) | {repo, path, name, size}]'
546
+ ```
547
+
548
+ ### Find artifacts not downloaded in 90 days
549
+
550
+ Only matches previously-downloaded items (see [Gotchas](#gotchas)).
551
+ Combine with the never-downloaded pattern above for full coverage.
552
+
553
+ ```
554
+ items.find({
555
+ "repo":"my-repo",
556
+ "type":"file",
557
+ "stat.downloaded":{"$before":"90d"}
558
+ }).include("name","repo","path","stat.downloaded","size")
559
+ ```
560
+
561
+ ### Find items by build name (cross-domain)
562
+
563
+ ```
564
+ items.find({"artifact.module.build.name":"my-service"})
565
+ .include("name","repo","path","artifact.module.build.number")
566
+ .sort({"$desc":["modified"]})
567
+ .limit(50)
568
+ ```
569
+
570
+ ### Find builds by name
571
+
572
+ Non-admin users must include `name`, `number`, `repo` — omitting any
573
+ produces an error.
574
+
575
+ ```
576
+ builds.find({"name":{"$match":"*my-service*"}})
577
+ .include("name","number","repo","started")
578
+ .sort({"$desc":["started"]})
579
+ .limit(10)
580
+ ```
581
+
582
+ ### Find build artifacts
583
+
584
+ ```
585
+ artifacts.find({"module.build.name":"my-service","module.build.number":"42"})
586
+ .include("name","type","sha1","md5")
587
+ ```
588
+
589
+ ### Find build dependencies
590
+
591
+ ```
592
+ dependencies.find({"module.build.name":"my-service","module.build.number":"42"})
593
+ .include("name","scope","type","sha1")
594
+ ```
595
+
596
+ ### Remote repository content
597
+
598
+ Remote repo artifacts are stored in a `-cache` suffixed repo. Always query
599
+ the cache repo, not the remote repo itself:
600
+
601
+ ```
602
+ items.find({"repo":"npm-remote-cache","name":{"$match":"*.tgz"}})
603
+ ```
604
+
605
+ ## Gotchas
606
+
607
+ - The request body is **plain text**, not JSON — use
608
+ `Content-Type: text/plain`.
609
+ - String values in criteria must be quoted, including numeric comparisons
610
+ (`"size":{"$gt":"1000"}` not `"size":{"$gt":1000}`).
611
+ - Remote repo content lives in `<repo>-cache`, not `<repo>`.
612
+ - Sort fields must appear in the result set (included explicitly or by
613
+ default).
614
+ - Non-admin `items` queries must return `repo`, `path`, `name`.
615
+ - Non-admin `builds` queries must return `name`, `number`, `repo`.
616
+ - Items connect to builds through checksum matching (SHA-1), so cross-domain
617
+ queries between items and builds are valid but traverse multiple joins.
618
+ - The `path` value for items at the **root** of a repository is `"."`, not
619
+ `""` or `"/"`. Use `"path":"."` to match root-level files.
620
+ - **Docker `list.manifest.json`** — multi-arch images store two manifest files per
621
+ tag: `manifest.json` (platform-specific manifest) and `list.manifest.json` (OCI
622
+ image index). Filtering by `"name":"manifest.json"` is correct for tag listing
623
+ (one result per tag), but silently excludes `list.manifest.json` entries. Use
624
+ `"name":{"$match":"*manifest.json"}` when querying by uploader, date range, or
625
+ any context where all manifest pushes should be counted.
626
+ - **`stat.downloads` filters do not match zero-download items** — never-downloaded
627
+ items lack a stats row so the join finds nothing. Use the client-side `jq`
628
+ approach in "Find never-downloaded files" above.
629
+ - `$match` uses SQL-style wildcards: `*` matches any characters, `?` matches
630
+ exactly one character. It is **not** regex. Literal `_` and `%` in patterns
631
+ are escaped automatically.
632
+ - The `builds.number` field is a **string**, not an integer. Build numbers
633
+ like `"42"`, `"1.0.3"`, and `"SNAPSHOT-1"` are all valid.
634
+ - Release bundle `type` values are uppercase strings: `"SOURCE"` or
635
+ `"TARGET"`.
636
+ - Dates accept both ISO 8601 format (`"2025-06-01T00:00:00.000Z"`) and
637
+ epoch milliseconds as a string (`"1719792000000"`).
638
+ - The server silently excludes trash, support-bundle, and in-transit
639
+ repository content from AQL results. If an item exists but doesn't appear
640
+ in results, it may be in one of these hidden repos.
641
+ - Virtual repo queries are rewritten to search the underlying physical repos.
642
+ The `repo` field in results shows the physical repo name, not the virtual
643
+ repo name you queried.
644
+
645
+ ## Official documentation
646
+
647
+ - [Artifactory Query Language](https://docs.jfrog.com/artifactory/docs/artifactory-query-language) — overview and architecture
648
+ - [Query Structure and Syntax](https://docs.jfrog.com/artifactory/docs/aql-syntax) — domain queries, field references, JSON-like syntax rules
649
+ - [Search Criteria and Operators](https://docs.jfrog.com/artifactory/docs/aql-search-criteria) — comparators, wildcards, `$msp`, relative time
650
+ - [AQL Entities and Fields Reference](https://docs.jfrog.com/artifactory/docs/aql-entities-fields-reference) — complete field list for all domains
651
+ - [Query Output and Modifiers](https://docs.jfrog.com/artifactory/docs/aql-query-output) — `.include()`, `.sort()`, `.offset()`, `.limit()`, `.distinct()`
652
+ - [Query Execution and Permissions](https://docs.jfrog.com/artifactory/docs/aql-query-execution) — authentication, scoped tokens, HTTP errors, streaming
653
+ - [AQL Examples and Common Patterns](https://docs.jfrog.com/artifactory/docs/aql-examples) — ready-to-use queries by use case
654
+ - [Repository-Specific Queries](https://docs.jfrog.com/artifactory/docs/aql-repository-queries) — `.transitive()`, virtual repos, remote search
655
+ - [Performance and Operational Controls](https://docs.jfrog.com/artifactory/docs/aql-performance) — result limits, timeouts, rate limiting, optimization
656
+