aemdm 0.4.1 → 0.5.0

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.
package/dist/cli.js CHANGED
@@ -39,6 +39,7 @@ const assetGetSchema = z
39
39
  output: z.string().optional(),
40
40
  metadata: z.boolean().default(false),
41
41
  imsToken: z.string().optional(),
42
+ mimeType: z.string().optional(),
42
43
  })
43
44
  .superRefine((value, ctx) => {
44
45
  if (value.metadata && value.output) {
@@ -193,6 +194,22 @@ async function handleAssetGet(assetId, options, runtime) {
193
194
  writeJson(runtime.stdout, metadata);
194
195
  return;
195
196
  }
197
+ let mimeType = parsed.mimeType;
198
+ if (!parsed.original && !parsed.format && !parsed.binary && !mimeType) {
199
+ if (imsToken) {
200
+ const metadata = await requestJson(buildMetadataUrl(baseUrl, assetId), {
201
+ imsToken,
202
+ fetchImpl: runtime.fetchImpl,
203
+ });
204
+ mimeType = metadata?.repositoryMetadata?.["dc:format"];
205
+ if (mimeType)
206
+ verbose(runtime, `detected mime type: ${mimeType}`);
207
+ }
208
+ else {
209
+ throw new CliError("Cannot determine asset type without authentication. " +
210
+ "Provide --format for images, --original for documents, or --ims-token to auto-detect.");
211
+ }
212
+ }
196
213
  const url = buildAssetUrl(baseUrl, {
197
214
  assetId,
198
215
  seoName: parsed.seoName,
@@ -202,6 +219,7 @@ async function handleAssetGet(assetId, options, runtime) {
202
219
  quality: parsed.quality,
203
220
  maxQuality: parsed.maxQuality,
204
221
  original: parsed.original,
222
+ mimeType,
205
223
  });
206
224
  if (!parsed.binary) {
207
225
  writeLine(runtime.stdout, url);
@@ -285,6 +303,7 @@ async function handleSearch(options, runtime) {
285
303
  return;
286
304
  }
287
305
  const dimensions = resolveDimensions(parsed.size, parsed.width, parsed.height);
306
+ const mimeType = firstHit.repositoryMetadata?.["dc:format"];
288
307
  const assetUrl = buildAssetUrl(baseUrl, {
289
308
  assetId,
290
309
  seoName: parsed.seoName,
@@ -294,6 +313,7 @@ async function handleSearch(options, runtime) {
294
313
  quality: parsed.quality,
295
314
  maxQuality: parsed.maxQuality,
296
315
  original: parsed.original,
316
+ mimeType,
297
317
  });
298
318
  if (parsed.firstUrl) {
299
319
  writeLine(runtime.stdout, assetUrl);
@@ -353,6 +373,33 @@ function buildProgram(runtime) {
353
373
  .version(pkg.version)
354
374
  .showHelpAfterError()
355
375
  .option("-v, --verbose", "Show additional diagnostic output")
376
+ .addHelpText("after", `
377
+ Bucket:
378
+ Most commands need a delivery bucket. Provide it in one of three ways:
379
+ 1. --bucket delivery-p123-e456.adobeaemcloud.com (per-command flag)
380
+ 2. AEMDM_BUCKET environment variable
381
+ 3. Save a default: aemdm --bucket delivery-p123-e456.adobeaemcloud.com
382
+ This writes to ~/.aemdm/config.json and is used when no flag or env var is set.
383
+
384
+ Authentication:
385
+ Public asset URLs and basic metadata (HEAD-based) require no authentication.
386
+ Full metadata, binary downloads, and search require an IMS bearer token:
387
+ 1. --ims-token <token> (per-command flag)
388
+ 2. AEMDM_IMS_TOKEN environment variable
389
+ 3. Save a default: aemdm --ims-token <token>
390
+ This writes the token to ~/.aemdm/config.json for reuse.
391
+
392
+ Search also requires an Adobe API key:
393
+ 1. --api-key <key> (per-command flag)
394
+ 2. AEMDM_API_KEY environment variable
395
+
396
+ Examples:
397
+ aemdm --bucket delivery-p123-e456.adobeaemcloud.com Save default bucket
398
+ aemdm asset get urn:aaid:aem:1234 Print delivery URL
399
+ aemdm asset get urn:aaid:aem:1234 --format webp Print URL with format
400
+ aemdm asset get urn:aaid:aem:1234 --metadata Fetch asset metadata
401
+ aemdm search --text "hero banner" Search by text
402
+ aemdm search --text "logo" --first-url --format png Search and get URL`)
356
403
  .configureOutput({
357
404
  writeOut: (text) => runtime.stdout.write(text),
358
405
  writeErr: (text) => runtime.stderr.write(text),
@@ -369,7 +416,8 @@ function buildProgram(runtime) {
369
416
  .option("--output <file>", "Output file path for --binary. Use - for stdout")
370
417
  .option("--metadata", "Fetch metadata JSON instead of building a URL")
371
418
  .option("--ims-token <token>", "IMS bearer token for metadata or binary requests")
372
- .action((assetId, options) => handleAssetGet(assetId, options, runtime));
419
+ .option("--mime-type <type>", "Asset MIME type (skips metadata lookup for route detection)")
420
+ .action((assetId, options, cmd) => handleAssetGet(assetId, { ...cmd.parent.opts(), ...options }, runtime));
373
421
  configureCommonDeliveryOptions(program.command("search").description("Search assets and optionally resolve the first result"))
374
422
  .option("--bucket <bucket-or-url>", "Bucket host or full bucket URL")
375
423
  .option("--ims-token <token>", "IMS bearer token")
@@ -401,12 +449,39 @@ Core commands:
401
449
  - aemdm asset get <assetId>
402
450
  - aemdm search
403
451
 
452
+ Bucket configuration (pick one):
453
+ - --bucket delivery-p123-e456.adobeaemcloud.com (per-command flag)
454
+ - AEMDM_BUCKET environment variable
455
+ - Save a default: aemdm --bucket delivery-p123-e456.adobeaemcloud.com
456
+ This writes to ~/.aemdm/config.json and is used when no flag or env var is set.
457
+
458
+ Authentication:
459
+ - Public asset URLs and basic metadata (HEAD-based) require no authentication.
460
+ - Full metadata, binary downloads, and search require an IMS bearer token (pick one):
461
+ 1. --ims-token <token> (per-command flag)
462
+ 2. AEMDM_IMS_TOKEN environment variable
463
+ 3. Save a default: aemdm --ims-token <token>
464
+ This writes the token to ~/.aemdm/config.json for reuse.
465
+ - Both bucket and token can be saved together: aemdm --bucket <host> --ims-token <token>
466
+ - Search also requires an Adobe API key via --api-key <key> or AEMDM_API_KEY.
467
+
468
+ URL format:
469
+ - Images: https://<bucket>/adobe/assets/<assetId>/as/<seoName>.<format>
470
+ - Documents/video: https://<bucket>/adobe/assets/<assetId>/original/as/<seoName>
471
+ - Default seo-name is "asset", default format is "png".
472
+ - Use --format to override (gif, png, jpg, jpeg, webp, avif).
473
+ - Use --seo-name to set a custom SEO-friendly name segment.
474
+
475
+ Route detection:
476
+ - When authenticated, asset get auto-detects the MIME type via the metadata endpoint.
477
+ Images get the /as/<seoName>.<format> route; documents/video get /original/as/<seoName>.
478
+ - When unauthenticated, you must specify --format (for images) or --original (for documents).
479
+ - Use --mime-type <type> to skip the metadata lookup when you already know the type
480
+ (e.g. from a prior search result's dc:format field).
481
+ - search --first-url auto-detects using the dc:format from the search hit (no extra call).
482
+
404
483
  Important defaults:
405
- - Bucket comes from --bucket or AEMDM_BUCKET.
406
- - A standalone call like aemdm --bucket delivery-p123-e456.adobeaemcloud.com saves the default bucket to the local aemdm profile config.
407
- - aemdm --ims-token <token> saves the IMS token to the profile config. Both can be saved together.
408
- - Search auth comes from --ims-token/AEMDM_IMS_TOKEN/profile config and --api-key/AEMDM_API_KEY.
409
- - asset get prints a URL by default.
484
+ - asset get prints a delivery URL by default.
410
485
  - asset get --metadata prints full JSON metadata when authenticated, or basic public JSON metadata when no token is supplied.
411
486
  - asset get --binary downloads the asset and requires --output.
412
487
  - search --first-id prints one asset ID for piping.
@@ -419,6 +494,7 @@ Asset URL examples:
419
494
  - aemdm asset get urn:aaid:aem:1234 --original --binary --output ./asset.bin
420
495
  - aemdm asset get urn:aaid:aem:1234 --metadata
421
496
  - aemdm asset get urn:aaid:aem:1234 --metadata --ims-token <token>
497
+ - aemdm asset get urn:aaid:aem:1234 --mime-type application/pdf
422
498
 
423
499
  Search examples:
424
500
  - aemdm search --text "hero banner"
@@ -444,8 +520,12 @@ LLM usage guidance:
444
520
  - Use search when you need to discover an asset by metadata or text.
445
521
  - Prefer --first-id or --ids-only when another CLI call needs asset IDs.
446
522
  - Prefer --first-url when the user wants a delivery URL from a search result.
523
+ --first-url uses the dc:format from the search hit to pick the correct route automatically.
447
524
  - Prefer --first-metadata when the user wants the resolved asset metadata after search.
448
525
  - Prefer --first-binary with --output when the user wants the downloaded file.
526
+ - When piping search results to asset get, pass --mime-type with the dc:format from the
527
+ search result to avoid an extra metadata call and ensure the correct route.
528
+ Example: aemdm asset get <id> --mime-type application/pdf
449
529
  `;
450
530
  }
451
531
  function parseStandaloneConfig(argv) {
@@ -5,6 +5,9 @@ export const TRANSFORM_FALLBACK_FORMAT = "png";
5
5
  export const deliveryFormatSchema = z.enum(["gif", "png", "jpg", "jpeg", "webp", "avif"]);
6
6
  const qualitySchema = z.number().int().min(1).max(100);
7
7
  const dimensionSchema = z.number().int().min(1);
8
+ function isImageMimeType(mimeType) {
9
+ return mimeType.startsWith("image/");
10
+ }
8
11
  export function parseSize(value) {
9
12
  const match = /^(?<width>\d+)?x(?<height>\d+)?$/i.exec(value.trim());
10
13
  if (!match?.groups) {
@@ -58,7 +61,9 @@ export function buildAssetUrl(baseUrl, options) {
58
61
  if (options.height !== undefined) {
59
62
  dimensionSchema.parse(options.height);
60
63
  }
61
- if (options.original) {
64
+ const useOriginal = options.original ||
65
+ (options.mimeType !== undefined && !isImageMimeType(options.mimeType));
66
+ if (useOriginal) {
62
67
  return `${normalizedBase}/${encodedAssetId}/original/as/${encodedSeoName}`;
63
68
  }
64
69
  const shouldUseTransformRoute = options.format !== undefined ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aemdm",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "CLI for Adobe Dynamic Media with OpenAPI",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Chris Pilsworth",
@@ -41,7 +41,7 @@
41
41
  "node": ">=20"
42
42
  },
43
43
  "dependencies": {
44
- "aemdm": "^0.4.0",
44
+ "aemdm": "^0.4.1",
45
45
  "commander": "^14.0.1",
46
46
  "zod": "^4.1.12"
47
47
  },