aemdm 0.1.3 → 0.3.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/README.md +7 -24
- package/dist/cli.js +84 -19
- package/dist/lib/client.js +4 -4
- package/dist/lib/config.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -189,36 +189,19 @@ aemdm search --raw-query @./query.json
|
|
|
189
189
|
|
|
190
190
|
## Publishing
|
|
191
191
|
|
|
192
|
-
|
|
192
|
+
The repository includes a `/release` skill for Claude Code that automates the full release process:
|
|
193
193
|
|
|
194
|
-
```bash
|
|
195
|
-
npm test
|
|
196
|
-
npm run pack:check
|
|
197
|
-
npm publish
|
|
198
194
|
```
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
### Automated npm publish from GitHub
|
|
203
|
-
|
|
204
|
-
The repository includes a GitHub Actions workflow that publishes to npm when you push a version tag like `v0.1.3` or publish a GitHub release for that tag.
|
|
205
|
-
|
|
206
|
-
Required setup:
|
|
207
|
-
|
|
208
|
-
```bash
|
|
209
|
-
NPM_TOKEN
|
|
195
|
+
/release patch # 0.2.1 → 0.2.2
|
|
196
|
+
/release minor # 0.2.1 → 0.3.0
|
|
197
|
+
/release major # 0.2.1 → 1.0.0
|
|
210
198
|
```
|
|
211
199
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
Release flow:
|
|
200
|
+
The skill bumps the version across all files, builds, tests, commits, pushes, creates a git tag, and creates a GitHub release.
|
|
215
201
|
|
|
216
|
-
|
|
217
|
-
git tag v0.1.3
|
|
218
|
-
git push origin v0.1.3
|
|
219
|
-
```
|
|
202
|
+
Publishing to npm is triggered automatically when a GitHub release is created. The workflow uses npm trusted publishing (OIDC) via the `npm` GitHub environment — no token secret is needed.
|
|
220
203
|
|
|
221
|
-
The workflow verifies that the tag matches the `version` field in `package.json`, runs `lint`, `build`, and `test`, and then publishes the package.
|
|
204
|
+
The workflow verifies that the release tag matches the `version` field in `package.json`, runs `lint`, `build`, and `test`, and then publishes the package with provenance.
|
|
222
205
|
|
|
223
206
|
## References
|
|
224
207
|
|
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
3
4
|
import { Command, CommanderError, Option } from "commander";
|
|
4
5
|
import { z } from "zod";
|
|
5
|
-
import { CliError, request, requestJson, resolveBucket, resolveOptionalImsToken, resolveSearchAuth, } from "./lib/client.js";
|
|
6
|
+
import { CliError, normalizeBucket, request, requestJson, resolveBucket, resolveOptionalImsToken, resolveSearchAuth, } from "./lib/client.js";
|
|
6
7
|
import { readProfileConfig, writeProfileConfig, } from "./lib/config.js";
|
|
7
8
|
import { buildAssetUrl, buildMetadataUrl, deliveryFormatSchema, resolveDimensions, } from "./lib/delivery.js";
|
|
8
9
|
import { buildSearchRequest, getAssetIdFromHit, loadRawQuery, } from "./lib/search.js";
|
|
9
10
|
import { formatSearchResults, writeBinaryOutput, writeJson, writeLine, } from "./lib/output.js";
|
|
11
|
+
function verbose(runtime, message) {
|
|
12
|
+
if (runtime.verbose) {
|
|
13
|
+
writeLine(runtime.stderr, `[verbose] ${message}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
10
16
|
const numberOption = (flagName) => (value) => {
|
|
11
17
|
const parsed = Number(value);
|
|
12
18
|
if (!Number.isInteger(parsed)) {
|
|
@@ -161,8 +167,19 @@ async function handleAssetGet(assetId, options, runtime) {
|
|
|
161
167
|
const parsed = parseWithSchema(assetGetSchema, options);
|
|
162
168
|
const profile = await readProfileConfig(runtime.env);
|
|
163
169
|
const baseUrl = resolveBucket(parsed.bucket, runtime.env, profile.bucket);
|
|
164
|
-
const imsToken = resolveOptionalImsToken(parsed.imsToken, runtime.env);
|
|
170
|
+
const imsToken = resolveOptionalImsToken(parsed.imsToken, runtime.env, profile.imsToken);
|
|
165
171
|
const dimensions = resolveDimensions(parsed.size, parsed.width, parsed.height);
|
|
172
|
+
verbose(runtime, `bucket: ${baseUrl}`);
|
|
173
|
+
verbose(runtime, `asset: ${assetId}`);
|
|
174
|
+
if (imsToken)
|
|
175
|
+
verbose(runtime, `auth: IMS token provided`);
|
|
176
|
+
if (dimensions.width || dimensions.height) {
|
|
177
|
+
verbose(runtime, `dimensions: ${dimensions.width ?? "auto"}x${dimensions.height ?? "auto"}`);
|
|
178
|
+
}
|
|
179
|
+
if (parsed.format)
|
|
180
|
+
verbose(runtime, `format: ${parsed.format}`);
|
|
181
|
+
if (parsed.quality)
|
|
182
|
+
verbose(runtime, `quality: ${parsed.quality}`);
|
|
166
183
|
if (parsed.metadata) {
|
|
167
184
|
const metadata = imsToken
|
|
168
185
|
? await requestJson(buildMetadataUrl(baseUrl, assetId), {
|
|
@@ -210,9 +227,21 @@ async function handleSearch(options, runtime) {
|
|
|
210
227
|
const parsed = parseWithSchema(searchSchema, options);
|
|
211
228
|
const profile = await readProfileConfig(runtime.env);
|
|
212
229
|
const baseUrl = resolveBucket(parsed.bucket, runtime.env, profile.bucket);
|
|
213
|
-
const { imsToken, apiKey } = resolveSearchAuth(parsed.imsToken, parsed.apiKey, runtime.env);
|
|
230
|
+
const { imsToken, apiKey } = resolveSearchAuth(parsed.imsToken, parsed.apiKey, runtime.env, profile.imsToken);
|
|
214
231
|
const searchBody = await buildSearchBody(parsed);
|
|
215
232
|
const searchUrl = `${baseUrl}/search`;
|
|
233
|
+
verbose(runtime, `bucket: ${baseUrl}`);
|
|
234
|
+
verbose(runtime, `search: POST ${searchUrl}`);
|
|
235
|
+
verbose(runtime, `api-key: ${apiKey}`);
|
|
236
|
+
verbose(runtime, `auth: IMS token provided`);
|
|
237
|
+
if (parsed.text)
|
|
238
|
+
verbose(runtime, `text: "${parsed.text}"`);
|
|
239
|
+
if (parsed.where.length > 0)
|
|
240
|
+
verbose(runtime, `where: ${parsed.where.join(", ")}`);
|
|
241
|
+
if (parsed.limit !== undefined)
|
|
242
|
+
verbose(runtime, `limit: ${parsed.limit}`);
|
|
243
|
+
if (parsed.rawQuery)
|
|
244
|
+
verbose(runtime, `raw-query: ${parsed.rawQuery}`);
|
|
216
245
|
const response = await requestJson(searchUrl, {
|
|
217
246
|
method: "POST",
|
|
218
247
|
imsToken,
|
|
@@ -220,6 +249,9 @@ async function handleSearch(options, runtime) {
|
|
|
220
249
|
jsonBody: searchBody,
|
|
221
250
|
fetchImpl: runtime.fetchImpl,
|
|
222
251
|
});
|
|
252
|
+
const resultCount = response.hits?.results?.length ?? 0;
|
|
253
|
+
const totalCount = response.search_metadata?.totalCount?.total;
|
|
254
|
+
verbose(runtime, `results: ${resultCount} returned${totalCount !== undefined ? ` (${totalCount} total)` : ""}`);
|
|
223
255
|
if (parsed.json) {
|
|
224
256
|
writeJson(runtime.stdout, response);
|
|
225
257
|
return;
|
|
@@ -315,13 +347,17 @@ function buildProgram(runtime) {
|
|
|
315
347
|
program
|
|
316
348
|
.name("aemdm")
|
|
317
349
|
.description("CLI for Adobe Dynamic Media with OpenAPI")
|
|
318
|
-
.version("0.
|
|
350
|
+
.version("0.3.0")
|
|
319
351
|
.showHelpAfterError()
|
|
352
|
+
.option("-v, --verbose", "Show additional diagnostic output")
|
|
320
353
|
.configureOutput({
|
|
321
354
|
writeOut: (text) => runtime.stdout.write(text),
|
|
322
355
|
writeErr: (text) => runtime.stderr.write(text),
|
|
323
356
|
})
|
|
324
|
-
.exitOverride()
|
|
357
|
+
.exitOverride()
|
|
358
|
+
.hook("preAction", () => {
|
|
359
|
+
runtime.verbose = program.opts().verbose === true;
|
|
360
|
+
});
|
|
325
361
|
configureCommonDeliveryOptions(program.command("asset").description("Asset operations"))
|
|
326
362
|
.command("get <assetId>")
|
|
327
363
|
.description("Build a delivery URL, fetch metadata, or download a binary for an asset")
|
|
@@ -365,7 +401,8 @@ Core commands:
|
|
|
365
401
|
Important defaults:
|
|
366
402
|
- Bucket comes from --bucket or AEMDM_BUCKET.
|
|
367
403
|
- A standalone call like aemdm --bucket delivery-p123-e456.adobeaemcloud.com saves the default bucket to the local aemdm profile config.
|
|
368
|
-
-
|
|
404
|
+
- aemdm --ims-token <token> saves the IMS token to the profile config. Both can be saved together.
|
|
405
|
+
- Search auth comes from --ims-token/AEMDM_IMS_TOKEN/profile config and --api-key/AEMDM_API_KEY.
|
|
369
406
|
- asset get prints a URL by default.
|
|
370
407
|
- asset get --metadata prints full JSON metadata when authenticated, or basic public JSON metadata when no token is supplied.
|
|
371
408
|
- asset get --binary downloads the asset and requires --output.
|
|
@@ -408,14 +445,31 @@ LLM usage guidance:
|
|
|
408
445
|
- Prefer --first-binary with --output when the user wants the downloaded file.
|
|
409
446
|
`;
|
|
410
447
|
}
|
|
411
|
-
function
|
|
412
|
-
|
|
413
|
-
|
|
448
|
+
function parseStandaloneConfig(argv) {
|
|
449
|
+
const config = {};
|
|
450
|
+
const args = [...argv];
|
|
451
|
+
while (args.length > 0) {
|
|
452
|
+
const arg = args.shift();
|
|
453
|
+
if (arg.startsWith("--bucket=")) {
|
|
454
|
+
config.bucket = arg.slice("--bucket=".length);
|
|
455
|
+
}
|
|
456
|
+
else if (arg === "--bucket" && args.length > 0) {
|
|
457
|
+
config.bucket = args.shift();
|
|
458
|
+
}
|
|
459
|
+
else if (arg.startsWith("--ims-token=")) {
|
|
460
|
+
config.imsToken = arg.slice("--ims-token=".length);
|
|
461
|
+
}
|
|
462
|
+
else if (arg === "--ims-token" && args.length > 0) {
|
|
463
|
+
config.imsToken = args.shift();
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
return undefined;
|
|
467
|
+
}
|
|
414
468
|
}
|
|
415
|
-
if (
|
|
416
|
-
return
|
|
469
|
+
if (!config.bucket && !config.imsToken) {
|
|
470
|
+
return undefined;
|
|
417
471
|
}
|
|
418
|
-
return
|
|
472
|
+
return config;
|
|
419
473
|
}
|
|
420
474
|
function toCliError(error) {
|
|
421
475
|
if (error instanceof CliError) {
|
|
@@ -442,14 +496,25 @@ export async function runCli(argv, runtimeOverrides = {}) {
|
|
|
442
496
|
stdout: process.stdout,
|
|
443
497
|
stderr: process.stderr,
|
|
444
498
|
fetchImpl: fetch,
|
|
499
|
+
verbose: false,
|
|
445
500
|
...runtimeOverrides,
|
|
446
501
|
};
|
|
447
502
|
try {
|
|
448
|
-
const
|
|
449
|
-
if (
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
|
|
503
|
+
const standaloneConfig = parseStandaloneConfig(argv);
|
|
504
|
+
if (standaloneConfig !== undefined) {
|
|
505
|
+
const existing = await readProfileConfig(runtime.env);
|
|
506
|
+
const merged = { ...existing };
|
|
507
|
+
if (standaloneConfig.bucket) {
|
|
508
|
+
merged.bucket = normalizeBucket(standaloneConfig.bucket);
|
|
509
|
+
}
|
|
510
|
+
if (standaloneConfig.imsToken) {
|
|
511
|
+
merged.imsToken = standaloneConfig.imsToken;
|
|
512
|
+
}
|
|
513
|
+
const configPath = await writeProfileConfig(runtime.env, merged);
|
|
514
|
+
if (merged.bucket)
|
|
515
|
+
writeLine(runtime.stdout, `Saved bucket: ${merged.bucket}`);
|
|
516
|
+
if (standaloneConfig.imsToken)
|
|
517
|
+
writeLine(runtime.stdout, `Saved IMS token to profile config`);
|
|
453
518
|
writeLine(runtime.stdout, `Config file: ${configPath}`);
|
|
454
519
|
return 0;
|
|
455
520
|
}
|
|
@@ -470,7 +535,7 @@ export async function runCli(argv, runtimeOverrides = {}) {
|
|
|
470
535
|
}
|
|
471
536
|
}
|
|
472
537
|
const executedDirectly = process.argv[1] !== undefined &&
|
|
473
|
-
import.meta.url ===
|
|
538
|
+
fileURLToPath(import.meta.url) === realpathSync(process.argv[1]);
|
|
474
539
|
if (executedDirectly) {
|
|
475
540
|
const exitCode = await runCli(process.argv.slice(2));
|
|
476
541
|
process.exitCode = exitCode;
|
package/dist/lib/client.js
CHANGED
|
@@ -40,11 +40,11 @@ export function resolveBucket(explicitValue, env, profileBucket) {
|
|
|
40
40
|
}
|
|
41
41
|
return normalizeBucket(bucket);
|
|
42
42
|
}
|
|
43
|
-
export function resolveOptionalImsToken(explicitValue, env) {
|
|
44
|
-
return explicitValue ?? env.AEMDM_IMS_TOKEN;
|
|
43
|
+
export function resolveOptionalImsToken(explicitValue, env, profileImsToken) {
|
|
44
|
+
return explicitValue ?? env.AEMDM_IMS_TOKEN ?? profileImsToken;
|
|
45
45
|
}
|
|
46
|
-
export function resolveSearchAuth(imsToken, apiKey, env) {
|
|
47
|
-
const resolvedImsToken = imsToken ?? env.AEMDM_IMS_TOKEN;
|
|
46
|
+
export function resolveSearchAuth(imsToken, apiKey, env, profileImsToken) {
|
|
47
|
+
const resolvedImsToken = imsToken ?? env.AEMDM_IMS_TOKEN ?? profileImsToken;
|
|
48
48
|
const resolvedApiKey = apiKey ?? env.AEMDM_API_KEY ?? "asset_search_service";
|
|
49
49
|
if (!resolvedImsToken) {
|
|
50
50
|
throw new CliError("Missing IMS token. Use --ims-token or set AEMDM_IMS_TOKEN.");
|
package/dist/lib/config.js
CHANGED