makefx 1.0.4 → 1.0.6

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 (3) hide show
  1. package/README.md +1 -1
  2. package/makefx.mjs +71 -56
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -176,4 +176,4 @@ Run `makefx --help` or `makefx help <command>` for command-specific options.
176
176
  - Media generation guide: https://makefx.app/docs/media-playbooks
177
177
  - Source: https://github.com/krasnoperov/inventory
178
178
 
179
- Version: 1.0.4
179
+ Version: 1.0.6
package/makefx.mjs CHANGED
@@ -7,7 +7,9 @@ import { spawn } from "node:child_process";
7
7
  import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
8
8
  import os from "node:os";
9
9
  import path from "node:path";
10
- import { createReadStream } from "node:fs";
10
+ import { createReadStream, createWriteStream } from "node:fs";
11
+ import { Readable } from "node:stream";
12
+ import { pipeline } from "node:stream/promises";
11
13
  import https from "node:https";
12
14
  //#region \0rolldown/runtime.js
13
15
  var __create = Object.create;
@@ -3215,7 +3217,7 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
3215
3217
  var net = __require("net");
3216
3218
  var tls = __require("tls");
3217
3219
  var { randomBytes: randomBytes$1, createHash: createHash$2 } = __require("crypto");
3218
- var { Duplex: Duplex$2, Readable } = __require("stream");
3220
+ var { Duplex: Duplex$2, Readable: Readable$1 } = __require("stream");
3219
3221
  var { URL: URL$1 } = __require("url");
3220
3222
  var PerMessageDeflate = require_permessage_deflate();
3221
3223
  var Receiver = require_receiver();
@@ -5452,17 +5454,11 @@ async function executeUpload(parsed, deps = defaultDeps$8) {
5452
5454
  if (config.token.expiresAt < Date.now()) throw new Error(`Token expired for ${env} environment. Run: ${loginCommandForEnvironment(env)}`);
5453
5455
  const baseUrl = deps.resolveBaseUrl(env);
5454
5456
  const accessToken = config.token.accessToken;
5455
- const ctx = {
5456
- spaceId,
5457
- baseUrl,
5458
- accessToken
5459
- };
5460
5457
  if (env === "local") process$1.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
5461
5458
  try {
5462
5459
  const fileStat = await deps.stat(filePath);
5463
5460
  if (!fileStat.isFile()) throw new Error(`"${filePath}" is not a file`);
5464
5461
  if (fileStat.size > 10485760) throw new Error(`File too large (${(fileStat.size / 1024 / 1024).toFixed(2)}MB). Maximum size: 10MB`);
5465
- await preflightSourceVariants(ctx, deps, sourceVariantIds);
5466
5462
  const fileBuffer = await deps.readFile(filePath);
5467
5463
  const fileName = path.basename(filePath);
5468
5464
  const formData = new FormData();
@@ -5603,29 +5599,6 @@ function parseCsvOption(value, optionName) {
5603
5599
  if (value === "true") throw new UploadUsageError(`${optionName} requires a value`);
5604
5600
  return value.split(",").map((entry) => entry.trim()).filter(Boolean);
5605
5601
  }
5606
- async function preflightSourceVariants(ctx, deps, sourceVariantIds) {
5607
- if (sourceVariantIds.length === 0) return;
5608
- const state = await fetchVariantState(ctx, deps);
5609
- for (const sourceVariantId of sourceVariantIds) if (!state.variantsById.has(sourceVariantId)) throw new Error(`Source variant not found in space: ${sourceVariantId}`);
5610
- }
5611
- async function fetchVariantState(ctx, deps) {
5612
- const assets = (await fetchJson$1(ctx, deps, `/api/spaces/${ctx.spaceId}/assets`)).assets ?? [];
5613
- const variantsById = /* @__PURE__ */ new Map();
5614
- await Promise.all(assets.map(async (asset) => {
5615
- const details = await fetchJson$1(ctx, deps, `/api/spaces/${ctx.spaceId}/assets/${encodeURIComponent(asset.id)}`);
5616
- for (const variant of details.variants ?? []) variantsById.set(variant.id, variant);
5617
- }));
5618
- return { variantsById };
5619
- }
5620
- async function fetchJson$1(ctx, deps, requestPath) {
5621
- const response = await deps.fetch(`${ctx.baseUrl}${requestPath}`, { headers: {
5622
- Authorization: `Bearer ${ctx.accessToken}`,
5623
- Accept: "application/json"
5624
- } });
5625
- const data = await response.json().catch(() => ({}));
5626
- if (!response.ok) throw new Error("error" in data ? data.error : `Request failed: ${response.status}`);
5627
- return data;
5628
- }
5629
5602
  function printUsage$8() {
5630
5603
  console.log(`
5631
5604
  Usage:
@@ -5873,11 +5846,40 @@ async function downloadFile(input) {
5873
5846
  }
5874
5847
  const headers = {};
5875
5848
  if (input.accessToken) headers.Authorization = `Bearer ${input.accessToken}`;
5876
- const response = await fetch(`${input.baseUrl}${input.requestPath}`, { headers });
5877
- if (!response.ok) throw new Error(`Failed to download ${input.requestPath}: ${response.status} ${response.statusText}`);
5878
- const buffer = new Uint8Array(await response.arrayBuffer());
5849
+ const requestUrl = `${input.baseUrl}${input.requestPath}`;
5850
+ let response;
5851
+ try {
5852
+ response = await fetch(requestUrl, { headers });
5853
+ } catch (error) {
5854
+ throw new Error(`Failed to download ${input.requestPath}: ${formatFetchError(error)}`);
5855
+ }
5856
+ if (!response.ok) {
5857
+ const errorText = await readErrorText(response);
5858
+ throw new Error(`Failed to download ${input.requestPath}: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
5859
+ }
5879
5860
  await mkdir(path.dirname(input.outputPath), { recursive: true });
5880
- await writeFile(input.outputPath, buffer);
5861
+ if (!response.body) throw new Error(`Failed to download ${input.requestPath}: response body was empty`);
5862
+ try {
5863
+ await pipeline(Readable.fromWeb(response.body), createWriteStream(input.outputPath));
5864
+ } catch (error) {
5865
+ throw new Error(`Failed to write download ${input.requestPath} to ${input.outputPath}: ${formatFetchError(error)}`);
5866
+ }
5867
+ return {
5868
+ headers: response.headers,
5869
+ requestUrl
5870
+ };
5871
+ }
5872
+ async function readErrorText(response) {
5873
+ try {
5874
+ return (await response.text()).slice(0, 500).trim();
5875
+ } catch {
5876
+ return "";
5877
+ }
5878
+ }
5879
+ function formatFetchError(error) {
5880
+ if (!(error instanceof Error)) return String(error);
5881
+ const cause = error.cause instanceof Error ? ` (${error.cause.message})` : "";
5882
+ return `${error.message}${cause}`;
5881
5883
  }
5882
5884
  async function downloadImage(input) {
5883
5885
  await downloadFile({
@@ -8679,24 +8681,24 @@ async function executeAssets(parsed, deps = defaultDeps$3) {
8679
8681
  const ref = parsed.positionals[1];
8680
8682
  if (!ref) throw new Error("Variant ID or legacy image key is required: makefx assets download <variant-id-or-legacy-image-key> -o <file>");
8681
8683
  const outputPath = getOutputPath(parsed);
8682
- const resolved = await resolveDownloadRef(ref, ctx, deps);
8683
- await deps.downloadFile({
8684
+ const resolved = resolveDownloadRef(ref, ctx);
8685
+ const metadata = getDownloadedVariantMetadata(resolved, await deps.downloadFile({
8684
8686
  baseUrl: ctx.baseUrl,
8685
8687
  accessToken: ctx.accessToken,
8686
8688
  requestPath: resolved.requestPath,
8687
8689
  outputPath,
8688
8690
  force: ctx.force
8689
- });
8691
+ }));
8690
8692
  await recordDownloadedMirror({
8691
8693
  ctx,
8692
8694
  deps,
8693
8695
  outputPath,
8694
- variant: resolved.variant
8696
+ variant: metadata.mirrorVariant
8695
8697
  });
8696
- deps.print(`Downloaded ${resolved.mediaKey} to ${outputPath}`);
8698
+ deps.print(`Downloaded ${metadata.mediaKey} to ${outputPath}`);
8697
8699
  return {
8698
8700
  type: "download",
8699
- mediaKey: resolved.mediaKey,
8701
+ mediaKey: metadata.mediaKey,
8700
8702
  outputPath,
8701
8703
  variant: resolved.variant
8702
8704
  };
@@ -8881,7 +8883,7 @@ async function fetchJson(ctx, deps, apiPath) {
8881
8883
  }
8882
8884
  return response.json();
8883
8885
  }
8884
- async function resolveDownloadRef(ref, ctx, deps) {
8886
+ function resolveDownloadRef(ref, ctx) {
8885
8887
  if (looksLikeStorageKey(ref)) {
8886
8888
  if (!isLegacyImageStorageKey(ref)) throw new Error("Direct media key downloads are not supported. Pass a variant ID so the authenticated media endpoint can authorize the download.");
8887
8889
  return {
@@ -8889,19 +8891,32 @@ async function resolveDownloadRef(ref, ctx, deps) {
8889
8891
  requestPath: `/api/images/${ref}`
8890
8892
  };
8891
8893
  }
8892
- const assets = await listAssets(ctx, deps);
8893
- for (const asset of assets) {
8894
- const variant = (await getAssetDetails(ctx, deps, asset.id)).variants.find((candidate) => candidate.id === ref);
8895
- if (!variant) continue;
8896
- const mediaKey = variant.media_key || variant.image_key;
8897
- if (mediaKey) return {
8898
- mediaKey,
8899
- requestPath: `/api/spaces/${encodeURIComponent(ctx.spaceId)}/variants/${encodeURIComponent(variant.id)}/media`,
8900
- variant
8901
- };
8902
- throw new Error(`Variant ${ref} has no downloadable media; status is ${variant.status}`);
8903
- }
8904
- throw new Error(`Variant not found in space ${ctx.spaceId}: ${ref}`);
8894
+ return {
8895
+ mediaKey: ref,
8896
+ requestPath: `/api/spaces/${encodeURIComponent(ctx.spaceId)}/variants/${encodeURIComponent(ref)}/media`
8897
+ };
8898
+ }
8899
+ function getDownloadedVariantMetadata(resolved, download) {
8900
+ if (resolved.variant) return {
8901
+ mediaKey: resolved.variant.media_key || resolved.variant.image_key || resolved.mediaKey,
8902
+ mirrorVariant: resolved.variant
8903
+ };
8904
+ const headers = download?.headers;
8905
+ const variantId = headers?.get("x-makefx-variant-id");
8906
+ const assetId = headers?.get("x-makefx-asset-id");
8907
+ const mediaKind = headers?.get("x-makefx-media-kind");
8908
+ const mediaKey = headers?.get("x-makefx-media-key") || resolved.mediaKey;
8909
+ if (!variantId || !assetId) return { mediaKey };
8910
+ return {
8911
+ mediaKey,
8912
+ mirrorVariant: {
8913
+ id: variantId,
8914
+ asset_id: assetId,
8915
+ media_kind: mediaKind || "image",
8916
+ media_key: mediaKey,
8917
+ image_key: mediaKey.startsWith("images/") ? mediaKey : null
8918
+ }
8919
+ };
8905
8920
  }
8906
8921
  function looksLikeStorageKey(value) {
8907
8922
  return value.includes("/") || [
@@ -9460,7 +9475,7 @@ Options:
9460
9475
  }
9461
9476
  //#endregion
9462
9477
  //#region src/cli/index.ts
9463
- var CLI_VERSION = "1.0.4+20bbdda462d5";
9478
+ var CLI_VERSION = "1.0.6+d87c3032db88";
9464
9479
  async function main() {
9465
9480
  const argv = process$1.argv.slice(2);
9466
9481
  if (argv[0] === "--") argv.shift();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "makefx",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Command-line interface for AI-assisted game asset production with Make Effects.",
5
5
  "license": "MIT",
6
6
  "type": "module",