@raster-app/cli 0.3.2 → 0.4.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 +4 -3
- package/dist/index.js +125 -28
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ npx -p @raster-app/cli raster whoami
|
|
|
22
22
|
raster auth login
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
`raster auth login` signs you in over OAuth (no API key needed). When a browser is available
|
|
25
|
+
`raster auth login` signs you in over OAuth (no API key needed). When a browser is available, it opens a **browser authorization-code flow** with a local loopback redirect; otherwise, over SSH or on a headless machine, it falls back to the **device authorization grant**, printing a short code and a URL you approve in a browser on any device. Pass `--browser` or `--device` to force either.
|
|
26
26
|
|
|
27
27
|
OAuth tokens (access + refresh) are stored in `~/.config/raster/config.json` with owner-only permissions. The CLI refreshes the access token automatically before it expires and rotates the refresh token; when the saved login can no longer be refreshed, it asks you to run `raster auth login` again.
|
|
28
28
|
|
|
@@ -90,13 +90,14 @@ Show the organizations, plan, and libraries the current credential can access. A
|
|
|
90
90
|
| Command | Description | Example |
|
|
91
91
|
| --- | --- | --- |
|
|
92
92
|
| `raster assets ls` | List assets; filter with `--tag` (up to 5), page with `--page`/`--page-size`. | `raster assets ls --library brand --tag sunset` |
|
|
93
|
-
| `raster assets get <assetId>` | Show one asset's metadata. | `raster assets get asset_123` |
|
|
93
|
+
| `raster assets get <assetId>` | Show one asset's metadata — including its variants, and duration plus stream details for video. | `raster assets get asset_123` |
|
|
94
94
|
| `raster assets search <query>` | Search across the organization (`--library` scopes to one). | `raster assets search "golden hour"` |
|
|
95
95
|
| `raster assets download <assetId>` | Download the file (`-o` path, `--force` to overwrite). | `raster assets download asset_123 -o photo.png` |
|
|
96
|
-
| `raster assets upload <files...>` | Upload local files (batched at 20 per request). | `raster assets upload ./photos/*.png` |
|
|
96
|
+
| `raster assets upload <files...>` | Upload local files (batched at 20 per request). Pass `--parent <assetId>` to attach a single image as a variant of an existing asset. | `raster assets upload ./photos/*.png` |
|
|
97
97
|
| `raster assets rm <assetIds...>` | Move assets to trash (`--yes` skips the prompt). | `raster assets rm asset_123 asset_456` |
|
|
98
98
|
| `raster assets describe <assetId> --text <text>` | Set an asset's description. | `raster assets describe asset_123 --text "Hero shot"` |
|
|
99
99
|
| `raster assets transfer <assetIds...> --to <libraryId>` | Move assets to another library. | `raster assets transfer asset_123 --to archive` |
|
|
100
|
+
| `raster assets promote <assetId> --variant <variantId>` | Make a variant the asset's default — the pixels change at the same URL, and the old default is kept as a variant. | `raster assets promote asset_123 --variant asset_456` |
|
|
100
101
|
|
|
101
102
|
### `tags`
|
|
102
103
|
|
package/dist/index.js
CHANGED
|
@@ -16337,11 +16337,11 @@ config(en_default());
|
|
|
16337
16337
|
// package.json
|
|
16338
16338
|
var package_default = {
|
|
16339
16339
|
name: "@raster-app/cli",
|
|
16340
|
-
version: "0.
|
|
16340
|
+
version: "0.4.0",
|
|
16341
16341
|
description: "Command-line client for the Raster API",
|
|
16342
16342
|
license: "Apache-2.0",
|
|
16343
16343
|
type: "module",
|
|
16344
|
-
homepage: "https://
|
|
16344
|
+
homepage: "https://raster.app/docs/api/cli",
|
|
16345
16345
|
engines: {
|
|
16346
16346
|
node: ">=20"
|
|
16347
16347
|
},
|
|
@@ -16372,7 +16372,7 @@ var package_default = {
|
|
|
16372
16372
|
};
|
|
16373
16373
|
|
|
16374
16374
|
// src/api/version.ts
|
|
16375
|
-
var API_VERSION = "2026-
|
|
16375
|
+
var API_VERSION = "2026-07-08";
|
|
16376
16376
|
var DEFAULT_BASE_URL = "https://api.raster.app";
|
|
16377
16377
|
|
|
16378
16378
|
// src/commands/assets.ts
|
|
@@ -16981,6 +16981,10 @@ import { join } from "node:path";
|
|
|
16981
16981
|
var MAX_UPLOAD_FILES = 20;
|
|
16982
16982
|
var MAX_TAGS_PER_QUERY = 5;
|
|
16983
16983
|
var MAX_DELETE_IDS = 100;
|
|
16984
|
+
var MAX_TRANSFER_IDS = 100;
|
|
16985
|
+
var MAX_TAG_IDS = 100;
|
|
16986
|
+
var MAX_TAGS_PER_CALL = 20;
|
|
16987
|
+
var MAX_DESCRIPTION_LENGTH = 2000;
|
|
16984
16988
|
var VISIBLE_KEY_PREFIX_LENGTH = 6;
|
|
16985
16989
|
function maskApiKey(key) {
|
|
16986
16990
|
return `${key.slice(0, VISIBLE_KEY_PREFIX_LENGTH)}…`;
|
|
@@ -16998,15 +17002,25 @@ function isErrnoException(error51) {
|
|
|
16998
17002
|
var CONTENT_TYPES_BY_EXTENSION = {
|
|
16999
17003
|
avif: "image/avif",
|
|
17000
17004
|
bmp: "image/bmp",
|
|
17005
|
+
fig: "application/x-figma",
|
|
17001
17006
|
gif: "image/gif",
|
|
17002
17007
|
heic: "image/heic",
|
|
17008
|
+
heif: "image/heif",
|
|
17009
|
+
ico: "image/x-icon",
|
|
17003
17010
|
jpeg: "image/jpeg",
|
|
17004
17011
|
jpg: "image/jpeg",
|
|
17005
17012
|
png: "image/png",
|
|
17006
17013
|
svg: "image/svg+xml",
|
|
17007
17014
|
tif: "image/tiff",
|
|
17008
17015
|
tiff: "image/tiff",
|
|
17009
|
-
webp: "image/webp"
|
|
17016
|
+
webp: "image/webp",
|
|
17017
|
+
avi: "video/x-msvideo",
|
|
17018
|
+
m4v: "video/mp4",
|
|
17019
|
+
mov: "video/quicktime",
|
|
17020
|
+
mp4: "video/mp4",
|
|
17021
|
+
mpeg: "video/mpeg",
|
|
17022
|
+
mpg: "video/mpeg",
|
|
17023
|
+
wmv: "video/x-ms-wmv"
|
|
17010
17024
|
};
|
|
17011
17025
|
var FALLBACK_CONTENT_TYPE = "application/octet-stream";
|
|
17012
17026
|
function contentTypeForFilename(filename) {
|
|
@@ -17560,14 +17574,21 @@ async function createCommandContext(globals, overrides = {}) {
|
|
|
17560
17574
|
};
|
|
17561
17575
|
const baseUrl = validateBaseUrl(env.RASTER_API_BASE_URL ?? DEFAULT_BASE_URL);
|
|
17562
17576
|
const baseFetch = overrides.fetch ?? globalThis.fetch;
|
|
17563
|
-
const credential = await resolveActiveCredential({
|
|
17577
|
+
const { credential, sessionExpired } = await resolveActiveCredential({
|
|
17564
17578
|
flagKey: globals.apiKey,
|
|
17565
17579
|
env,
|
|
17566
17580
|
config: config2,
|
|
17567
17581
|
fetch: instrumentFetch(baseFetch, output, null),
|
|
17568
17582
|
now: now()
|
|
17569
17583
|
});
|
|
17584
|
+
if (sessionExpired) {
|
|
17585
|
+
note(output, "Your saved login could not be refreshed and has been cleared.");
|
|
17586
|
+
if (config2.apiKey) {
|
|
17587
|
+
note(output, "The API key stored in config is not used automatically. Pass --api-key, or set RASTER_API_KEY, to use it.");
|
|
17588
|
+
}
|
|
17589
|
+
}
|
|
17570
17590
|
const apiKey = credential?.kind === "apiKey" ? { key: credential.key, source: credential.source } : null;
|
|
17591
|
+
const notAuthenticated = () => new AuthSetupError(authErrorMessage(sessionExpired));
|
|
17571
17592
|
const bearer = credentialBearer(credential);
|
|
17572
17593
|
const instrumentedFetch = instrumentFetch(baseFetch, output, bearer ? maskApiKey(bearer) : null);
|
|
17573
17594
|
const clientFactory = (clientOverrides) => createApiClient({
|
|
@@ -17578,9 +17599,8 @@ async function createCommandContext(globals, overrides = {}) {
|
|
|
17578
17599
|
let viewerPromise = null;
|
|
17579
17600
|
const loadViewer = () => {
|
|
17580
17601
|
if (!viewerPromise) {
|
|
17581
|
-
if (!credential)
|
|
17582
|
-
throw
|
|
17583
|
-
}
|
|
17602
|
+
if (!credential)
|
|
17603
|
+
throw notAuthenticated();
|
|
17584
17604
|
viewerPromise = unwrapResult(() => clientFactory().GET("/me", { params: { header: VERSION_HEADER } }));
|
|
17585
17605
|
}
|
|
17586
17606
|
return viewerPromise;
|
|
@@ -17629,6 +17649,7 @@ async function createCommandContext(globals, overrides = {}) {
|
|
|
17629
17649
|
isInteractive: overrides.isInteractive ?? process.stdin.isTTY === true,
|
|
17630
17650
|
credential,
|
|
17631
17651
|
apiKey,
|
|
17652
|
+
sessionExpired,
|
|
17632
17653
|
bearerToken: bearer,
|
|
17633
17654
|
fetch: instrumentedFetch,
|
|
17634
17655
|
clientFactory,
|
|
@@ -17663,7 +17684,7 @@ function credentialBearer(credential) {
|
|
|
17663
17684
|
async function resolveActiveCredential(options) {
|
|
17664
17685
|
const credential = resolveCredential({ flagKey: options.flagKey, env: options.env, config: options.config });
|
|
17665
17686
|
if (credential?.kind !== "oauth")
|
|
17666
|
-
return credential;
|
|
17687
|
+
return { credential, sessionExpired: false };
|
|
17667
17688
|
const session = await ensureFreshOAuthSession({
|
|
17668
17689
|
tokens: credential.tokens,
|
|
17669
17690
|
env: options.env,
|
|
@@ -17672,13 +17693,9 @@ async function resolveActiveCredential(options) {
|
|
|
17672
17693
|
now: options.now
|
|
17673
17694
|
});
|
|
17674
17695
|
if (session.status === "reauth-required") {
|
|
17675
|
-
return
|
|
17676
|
-
flagKey: options.flagKey,
|
|
17677
|
-
env: options.env,
|
|
17678
|
-
config: { ...options.config, oauth: undefined }
|
|
17679
|
-
});
|
|
17696
|
+
return { credential: null, sessionExpired: true };
|
|
17680
17697
|
}
|
|
17681
|
-
return { kind: "oauth", tokens: session.tokens };
|
|
17698
|
+
return { credential: { kind: "oauth", tokens: session.tokens }, sessionExpired: false };
|
|
17682
17699
|
}
|
|
17683
17700
|
function instrumentFetch(baseFetch, output, maskedKey) {
|
|
17684
17701
|
return async (input, init) => {
|
|
@@ -17696,15 +17713,18 @@ function instrumentFetch(baseFetch, output, maskedKey) {
|
|
|
17696
17713
|
}
|
|
17697
17714
|
};
|
|
17698
17715
|
}
|
|
17716
|
+
function authErrorMessage(sessionExpired) {
|
|
17717
|
+
return sessionExpired ? "Your saved login expired. Run `raster auth login` to sign in again." : "Not authenticated. Run `raster auth login` or set RASTER_API_KEY.";
|
|
17718
|
+
}
|
|
17699
17719
|
function requireAuth(context) {
|
|
17700
17720
|
if (context.credential)
|
|
17701
17721
|
return context.credential;
|
|
17702
|
-
throw new AuthSetupError(
|
|
17722
|
+
throw new AuthSetupError(authErrorMessage(context.sessionExpired));
|
|
17703
17723
|
}
|
|
17704
17724
|
function requireBearer(context) {
|
|
17705
17725
|
if (context.bearerToken)
|
|
17706
17726
|
return context.bearerToken;
|
|
17707
|
-
throw new AuthSetupError(
|
|
17727
|
+
throw new AuthSetupError(authErrorMessage(context.sessionExpired));
|
|
17708
17728
|
}
|
|
17709
17729
|
async function executeCommand(context, action) {
|
|
17710
17730
|
try {
|
|
@@ -17786,7 +17806,10 @@ async function getAssetCommand(context, assetId) {
|
|
|
17786
17806
|
printJson(context, asset);
|
|
17787
17807
|
return;
|
|
17788
17808
|
}
|
|
17789
|
-
renderRecord(context,
|
|
17809
|
+
renderRecord(context, assetRecordFields(asset));
|
|
17810
|
+
}
|
|
17811
|
+
function assetRecordFields(asset) {
|
|
17812
|
+
const fields = [
|
|
17790
17813
|
{ label: "id", value: asset.id },
|
|
17791
17814
|
{ label: "name", value: asset.name ?? "" },
|
|
17792
17815
|
{ label: "type", value: asset.contentType ?? "" },
|
|
@@ -17795,7 +17818,39 @@ async function getAssetCommand(context, assetId) {
|
|
|
17795
17818
|
{ label: "tags", value: (asset.tags ?? []).filter((tag) => tag !== null).join(",") },
|
|
17796
17819
|
{ label: "description", value: asset.description ?? "" },
|
|
17797
17820
|
{ label: "url", value: asset.url ?? "" }
|
|
17798
|
-
]
|
|
17821
|
+
];
|
|
17822
|
+
const push = (label, value) => {
|
|
17823
|
+
if (value)
|
|
17824
|
+
fields.push({ label, value });
|
|
17825
|
+
};
|
|
17826
|
+
push("app url", asset.appUrl);
|
|
17827
|
+
push("duration", formatDuration(asset.duration));
|
|
17828
|
+
push("parent", asset.parentId);
|
|
17829
|
+
const variants = (asset.variants ?? []).filter((variant) => variant !== null);
|
|
17830
|
+
if (variants.length > 0) {
|
|
17831
|
+
fields.push({ label: "variants", value: variants.map((variant) => variant.id).join(",") });
|
|
17832
|
+
}
|
|
17833
|
+
push("approval", asset.approval?.state);
|
|
17834
|
+
const capture = asset.metadata?.capture;
|
|
17835
|
+
push("captured", capture?.recordedAt);
|
|
17836
|
+
push("device", [capture?.make, capture?.model].filter(Boolean).join(" "));
|
|
17837
|
+
const video = asset.metadata?.video;
|
|
17838
|
+
if (video) {
|
|
17839
|
+
push("codec", video.codec);
|
|
17840
|
+
push("frame rate", video.frameRate ? `${video.frameRate} fps` : "");
|
|
17841
|
+
push("audio", video.hasAudio ? [video.audioCodec, video.audioLayout].filter(Boolean).join(" ") || "yes" : "none");
|
|
17842
|
+
}
|
|
17843
|
+
return fields;
|
|
17844
|
+
}
|
|
17845
|
+
function formatDuration(seconds) {
|
|
17846
|
+
if (typeof seconds !== "number" || !Number.isFinite(seconds) || seconds < 0)
|
|
17847
|
+
return "";
|
|
17848
|
+
const whole = Math.round(seconds);
|
|
17849
|
+
const hours = Math.floor(whole / 3600);
|
|
17850
|
+
const minutes = Math.floor(whole % 3600 / 60);
|
|
17851
|
+
const remainder = whole % 60;
|
|
17852
|
+
const padded = (value) => String(value).padStart(2, "0");
|
|
17853
|
+
return hours > 0 ? `${hours}:${padded(minutes)}:${padded(remainder)}` : `${minutes}:${padded(remainder)}`;
|
|
17799
17854
|
}
|
|
17800
17855
|
async function fetchAsset(context, assetId) {
|
|
17801
17856
|
const organizationId = await context.resolveOrg();
|
|
@@ -17862,9 +17917,15 @@ async function downloadAssetCommand(context, assetId, options) {
|
|
|
17862
17917
|
var uploadedAssetsSchema = exports_external.object({
|
|
17863
17918
|
assets: exports_external.array(exports_external.looseObject({ id: exports_external.string().optional(), name: exports_external.string().nullish() }))
|
|
17864
17919
|
});
|
|
17865
|
-
|
|
17920
|
+
var uploadOptionsSchema = exports_external.object({
|
|
17921
|
+
parent: exports_external.string().min(1).optional()
|
|
17922
|
+
});
|
|
17923
|
+
async function uploadAssetsCommand(context, files, options = {}) {
|
|
17866
17924
|
if (files.length === 0)
|
|
17867
17925
|
throw new UsageError("Provide at least one file to upload.");
|
|
17926
|
+
if (options.parent && files.length !== 1) {
|
|
17927
|
+
throw new ValidationError("--parent uploads a single file. Pass exactly one path.");
|
|
17928
|
+
}
|
|
17868
17929
|
const bearer = requireBearer(context);
|
|
17869
17930
|
const organizationId = await context.resolveOrg();
|
|
17870
17931
|
const libraryId = await context.resolveLibrary();
|
|
@@ -17882,6 +17943,8 @@ async function uploadAssetsCommand(context, files) {
|
|
|
17882
17943
|
}
|
|
17883
17944
|
formData.append("files", new Blob([bytes], { type: contentTypeForFilename(filePath) }), basename(filePath));
|
|
17884
17945
|
}
|
|
17946
|
+
if (options.parent)
|
|
17947
|
+
formData.append("parentId", options.parent);
|
|
17885
17948
|
if (batches.length > 1) {
|
|
17886
17949
|
note(context, `Uploading batch ${batchIndex + 1}/${batches.length} (${batch.length} files)…`);
|
|
17887
17950
|
}
|
|
@@ -17897,7 +17960,11 @@ async function uploadAssetsCommand(context, files) {
|
|
|
17897
17960
|
printJson(context, payloads.length === 1 ? payloads[0] : payloads);
|
|
17898
17961
|
return;
|
|
17899
17962
|
}
|
|
17900
|
-
|
|
17963
|
+
if (options.parent) {
|
|
17964
|
+
note(context, `Uploaded ${uploadedNames.length} variant of ${options.parent}.`);
|
|
17965
|
+
} else {
|
|
17966
|
+
note(context, `Uploaded ${uploadedNames.length} asset(s).`);
|
|
17967
|
+
}
|
|
17901
17968
|
for (const name of uploadedNames) {
|
|
17902
17969
|
context.stdout(`${name}
|
|
17903
17970
|
`);
|
|
@@ -17934,7 +18001,7 @@ async function removeAssetsCommand(context, assetIds, options) {
|
|
|
17934
18001
|
note(context, `Moved ${assetIds.length} asset(s) to trash.`);
|
|
17935
18002
|
}
|
|
17936
18003
|
var describeOptionsSchema = exports_external.object({
|
|
17937
|
-
text: exports_external.string().min(1)
|
|
18004
|
+
text: exports_external.string().min(1).max(MAX_DESCRIPTION_LENGTH)
|
|
17938
18005
|
});
|
|
17939
18006
|
async function describeAssetCommand(context, assetId, options) {
|
|
17940
18007
|
requireAuth(context);
|
|
@@ -17960,6 +18027,9 @@ var transferOptionsSchema = exports_external.object({
|
|
|
17960
18027
|
async function transferAssetsCommand(context, assetIds, options) {
|
|
17961
18028
|
if (assetIds.length === 0)
|
|
17962
18029
|
throw new UsageError("Provide at least one asset id.");
|
|
18030
|
+
if (assetIds.length > MAX_TRANSFER_IDS) {
|
|
18031
|
+
throw new ValidationError(`At most ${MAX_TRANSFER_IDS} asset ids are supported per call.`);
|
|
18032
|
+
}
|
|
17963
18033
|
requireAuth(context);
|
|
17964
18034
|
const organizationId = await context.resolveOrg();
|
|
17965
18035
|
const libraryId = await context.resolveLibrary();
|
|
@@ -17974,15 +18044,34 @@ async function transferAssetsCommand(context, assetIds, options) {
|
|
|
17974
18044
|
}
|
|
17975
18045
|
note(context, `Transferred ${assetIds.length} asset(s) to library ${options.to}.`);
|
|
17976
18046
|
}
|
|
18047
|
+
var promoteOptionsSchema = exports_external.object({
|
|
18048
|
+
variant: exports_external.string().min(1)
|
|
18049
|
+
});
|
|
18050
|
+
async function promoteVariantCommand(context, assetId, options) {
|
|
18051
|
+
requireAuth(context);
|
|
18052
|
+
const organizationId = await context.resolveOrg();
|
|
18053
|
+
const libraryId = await context.resolveLibrary();
|
|
18054
|
+
const client = context.clientFactory();
|
|
18055
|
+
const result = await unwrapResult(() => client.POST("/organizations/{organizationId}/libraries/{libraryId}/assets/{assetId}/promote", {
|
|
18056
|
+
params: { path: { organizationId, libraryId, assetId }, header: VERSION_HEADER },
|
|
18057
|
+
body: { variantId: options.variant }
|
|
18058
|
+
}));
|
|
18059
|
+
if (context.json) {
|
|
18060
|
+
printJson(context, result);
|
|
18061
|
+
return;
|
|
18062
|
+
}
|
|
18063
|
+
note(context, `Promoted variant ${result.promotedVariantId}; ${result.demotedVariantId} is kept as a variant. ` + "The swap completes a moment later — re-fetch the asset to see it.");
|
|
18064
|
+
}
|
|
17977
18065
|
function registerAssetCommands(program2, runAction) {
|
|
17978
18066
|
const assets = program2.command("assets").description("Work with assets in a library");
|
|
17979
18067
|
assets.command("ls").description("List assets in the library").option("--page <number>", "Page number (1-based)").option("--page-size <number>", "Results per page").option("--tag <tag...>", "Filter by tag (repeatable, up to 5)").action((options, command) => runAction(command, (context) => listAssetsCommand(context, parseOptions(listOptionsSchema, options))));
|
|
17980
18068
|
assets.command("get").description("Show a single asset").argument("<assetId>", "Asset id").action((assetId, _options, command) => runAction(command, (context) => getAssetCommand(context, assetId)));
|
|
17981
18069
|
assets.command("search").description("Search assets across the organization (pass --library to scope to one)").argument("<query>", "Search query").option("--page <number>", "Page number (1-based)").option("--page-size <number>", "Results per page").action((query, options, command) => runAction(command, (context) => searchAssetsCommand(context, query, parseOptions(searchOptionsSchema, options))));
|
|
17982
18070
|
assets.command("download").description("Download an asset's file").argument("<assetId>", "Asset id").option("-o, --output <path>", "Target file path").option("--force", "Overwrite the target file if it exists").action((assetId, options, command) => runAction(command, (context) => downloadAssetCommand(context, assetId, parseOptions(downloadOptionsSchema, options))));
|
|
17983
|
-
assets.command("upload").description("Upload local files as assets").argument("<files...>", "Local file paths").action((files,
|
|
18071
|
+
assets.command("upload").description("Upload local files as assets").argument("<files...>", "Local file paths").option("--parent <assetId>", "Attach a single image as a variant of this asset").action((files, options, command) => runAction(command, (context) => uploadAssetsCommand(context, files, parseOptions(uploadOptionsSchema, options))));
|
|
17984
18072
|
assets.command("rm").description("Move assets to trash (recoverable)").argument("<assetIds...>", "Asset ids").option("--yes", "Skip the confirmation prompt").action((assetIds, options, command) => runAction(command, (context) => removeAssetsCommand(context, assetIds, parseOptions(removeOptionsSchema, options))));
|
|
17985
18073
|
assets.command("describe").description("Set an asset's description").argument("<assetId>", "Asset id").requiredOption("--text <description>", "Description text").action((assetId, options, command) => runAction(command, (context) => describeAssetCommand(context, assetId, parseOptions(describeOptionsSchema, options))));
|
|
18074
|
+
assets.command("promote").description("Make one of an asset's variants its default (same URL, new pixels)").argument("<assetId>", "Asset id").requiredOption("--variant <variantId>", "Variant to promote").action((assetId, options, command) => runAction(command, (context) => promoteVariantCommand(context, assetId, parseOptions(promoteOptionsSchema, options))));
|
|
17986
18075
|
assets.command("transfer").description("Move assets to another library").argument("<assetIds...>", "Asset ids to move").requiredOption("--to <libraryId>", "Target library id").action((assetIds, options, command) => runAction(command, (context) => transferAssetsCommand(context, assetIds, parseOptions(transferOptionsSchema, options))));
|
|
17987
18076
|
}
|
|
17988
18077
|
|
|
@@ -18376,10 +18465,10 @@ async function logoutCommand(context) {
|
|
|
18376
18465
|
async function statusCommand(context) {
|
|
18377
18466
|
if (!context.credential) {
|
|
18378
18467
|
if (context.json) {
|
|
18379
|
-
printJson(context, { authenticated: false });
|
|
18468
|
+
printJson(context, { authenticated: false, sessionExpired: context.sessionExpired });
|
|
18380
18469
|
return;
|
|
18381
18470
|
}
|
|
18382
|
-
note(context, "Not authenticated. Run `raster auth login`.");
|
|
18471
|
+
note(context, context.sessionExpired ? "Your saved login expired. Run `raster auth login` to sign in again." : "Not authenticated. Run `raster auth login`.");
|
|
18383
18472
|
return;
|
|
18384
18473
|
}
|
|
18385
18474
|
if (context.credential.kind === "oauth") {
|
|
@@ -18598,9 +18687,18 @@ async function listTagsCommand(context, options) {
|
|
|
18598
18687
|
var mutateOptionsSchema = exports_external.object({
|
|
18599
18688
|
tag: exports_external.array(exports_external.string().min(1)).min(1)
|
|
18600
18689
|
});
|
|
18601
|
-
|
|
18690
|
+
function assertWithinTagCeilings(assetIds, tags) {
|
|
18602
18691
|
if (assetIds.length === 0)
|
|
18603
18692
|
throw new UsageError("Provide at least one asset id.");
|
|
18693
|
+
if (assetIds.length > MAX_TAG_IDS) {
|
|
18694
|
+
throw new ValidationError(`At most ${MAX_TAG_IDS} asset ids are supported per call.`);
|
|
18695
|
+
}
|
|
18696
|
+
if (tags.length > MAX_TAGS_PER_CALL) {
|
|
18697
|
+
throw new ValidationError(`At most ${MAX_TAGS_PER_CALL} tags are supported per call.`);
|
|
18698
|
+
}
|
|
18699
|
+
}
|
|
18700
|
+
async function tagAssetsCommand(context, assetIds, options) {
|
|
18701
|
+
assertWithinTagCeilings(assetIds, options.tag);
|
|
18604
18702
|
requireAuth(context);
|
|
18605
18703
|
const organizationId = await context.resolveOrg();
|
|
18606
18704
|
const libraryId = await context.resolveLibrary();
|
|
@@ -18616,8 +18714,7 @@ async function tagAssetsCommand(context, assetIds, options) {
|
|
|
18616
18714
|
note(context, `Tagged ${assetIds.length} asset(s) with ${options.tag.join(", ")}.`);
|
|
18617
18715
|
}
|
|
18618
18716
|
async function untagAssetsCommand(context, assetIds, options) {
|
|
18619
|
-
|
|
18620
|
-
throw new UsageError("Provide at least one asset id.");
|
|
18717
|
+
assertWithinTagCeilings(assetIds, options.tag);
|
|
18621
18718
|
requireAuth(context);
|
|
18622
18719
|
const organizationId = await context.resolveOrg();
|
|
18623
18720
|
const libraryId = await context.resolveLibrary();
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raster-app/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Command-line client for the Raster API",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://raster.app/docs/api/cli",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=20"
|
|
10
10
|
},
|