@playtagon/cli 0.3.5 → 0.4.1

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 CHANGED
@@ -30,7 +30,23 @@ playtagon logout
30
30
 
31
31
  ## Uploading Spine Assets
32
32
 
33
- ### Basic Upload
33
+ ### Auto-Upload with File Watcher (Recommended)
34
+
35
+ Start watching a directory for new exports. Files will automatically upload when you export from Spine Editor:
36
+
37
+ ```bash
38
+ playtagon spine watch ./exports --studio my-studio --game my-game
39
+ ```
40
+
41
+ This command runs in the background and automatically uploads files whenever Spine creates a new export. Just keep it running in a terminal while you work.
42
+
43
+ **Options:**
44
+ - `--batch` - Enable batch mode for multiple skeletons sharing one atlas
45
+ - `--interval <seconds>` - Change check interval (default: 2 seconds)
46
+
47
+ Press `Ctrl+C` to stop watching.
48
+
49
+ ### Manual Upload
34
50
 
35
51
  Upload a Spine asset directory to a specific studio and game:
36
52
 
@@ -157,7 +173,8 @@ The CLI tracks synced assets in `.sync-manifest.json`. Subsequent syncs only dow
157
173
  | `playtagon whoami` | Show current user info |
158
174
  | `playtagon setup` | Interactive setup wizard |
159
175
  | `playtagon config` | Manage configuration |
160
- | `playtagon spine upload` | Upload Spine assets |
176
+ | `playtagon spine upload` | Upload Spine assets (manual) |
177
+ | `playtagon spine watch` | Auto-upload on file changes (recommended) |
161
178
  | `playtagon spine validate` | Validate Spine files |
162
179
  | `playtagon spine preset` | Output export preset JSON |
163
180
  | `playtagon spine sync` | Sync approved assets with codegen |
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command11 } from "commander";
4
+ import { Command as Command12 } from "commander";
5
5
 
6
6
  // src/commands/login.ts
7
7
  import { Command } from "commander";
@@ -244,7 +244,7 @@ async function loginWithEmail(email, password) {
244
244
  });
245
245
  }
246
246
  async function loginWithBrowser() {
247
- return new Promise((resolve5) => {
247
+ return new Promise((resolve6) => {
248
248
  const port = 19419;
249
249
  const redirectUri = `http://localhost:${port}/callback`;
250
250
  const codeVerifier = generateCodeVerifier();
@@ -274,7 +274,7 @@ async function loginWithBrowser() {
274
274
  res.writeHead(200, { "Content-Type": "text/html" });
275
275
  res.end(getErrorHtml(errorDescription || error));
276
276
  cleanup();
277
- resolve5({ success: false, error: errorDescription || error });
277
+ resolve6({ success: false, error: errorDescription || error });
278
278
  return;
279
279
  }
280
280
  const accessToken = url.searchParams.get("access_token");
@@ -288,7 +288,7 @@ async function loginWithBrowser() {
288
288
  res.writeHead(200, { "Content-Type": "text/html" });
289
289
  res.end(getSuccessHtml(""));
290
290
  cleanup();
291
- resolve5({ success: true });
291
+ resolve6({ success: true });
292
292
  return;
293
293
  }
294
294
  const code = url.searchParams.get("code");
@@ -300,7 +300,7 @@ async function loginWithBrowser() {
300
300
  res.writeHead(200, { "Content-Type": "text/html" });
301
301
  res.end(getErrorHtml(exchangeError?.message || "Failed to exchange code"));
302
302
  cleanup();
303
- resolve5({ success: false, error: exchangeError?.message || "Failed to exchange code" });
303
+ resolve6({ success: false, error: exchangeError?.message || "Failed to exchange code" });
304
304
  return;
305
305
  }
306
306
  credentials.save({
@@ -313,19 +313,19 @@ async function loginWithBrowser() {
313
313
  res.writeHead(200, { "Content-Type": "text/html" });
314
314
  res.end(getSuccessHtml(data.user?.email || ""));
315
315
  cleanup();
316
- resolve5({ success: true });
316
+ resolve6({ success: true });
317
317
  } catch (err) {
318
318
  res.writeHead(200, { "Content-Type": "text/html" });
319
319
  res.end(getErrorHtml(err instanceof Error ? err.message : "Unknown error"));
320
320
  cleanup();
321
- resolve5({ success: false, error: err instanceof Error ? err.message : "Unknown error" });
321
+ resolve6({ success: false, error: err instanceof Error ? err.message : "Unknown error" });
322
322
  }
323
323
  return;
324
324
  }
325
325
  res.writeHead(200, { "Content-Type": "text/html" });
326
326
  res.end(getErrorHtml("No authorization token received"));
327
327
  cleanup();
328
- resolve5({ success: false, error: "No authorization token received" });
328
+ resolve6({ success: false, error: "No authorization token received" });
329
329
  } else {
330
330
  res.writeHead(404);
331
331
  res.end("Not found");
@@ -337,15 +337,15 @@ async function loginWithBrowser() {
337
337
  server.on("error", (err) => {
338
338
  if (err.code === "EADDRINUSE") {
339
339
  cleanup();
340
- resolve5({ success: false, error: `Port ${port} is already in use. Please close any applications using it.` });
340
+ resolve6({ success: false, error: `Port ${port} is already in use. Please close any applications using it.` });
341
341
  } else {
342
342
  cleanup();
343
- resolve5({ success: false, error: err.message });
343
+ resolve6({ success: false, error: err.message });
344
344
  }
345
345
  });
346
346
  timeoutId = setTimeout(() => {
347
347
  cleanup();
348
- resolve5({ success: false, error: "Authentication timed out. Please try again." });
348
+ resolve6({ success: false, error: "Authentication timed out. Please try again." });
349
349
  }, 5 * 60 * 1e3);
350
350
  });
351
351
  }
@@ -497,8 +497,8 @@ async function promptEmailLogin() {
497
497
  input: process.stdin,
498
498
  output: process.stdout
499
499
  });
500
- const question = (prompt) => new Promise((resolve5) => {
501
- rl.question(prompt, resolve5);
500
+ const question = (prompt) => new Promise((resolve6) => {
501
+ rl.question(prompt, resolve6);
502
502
  });
503
503
  try {
504
504
  const email = await question("Email: ");
@@ -590,7 +590,7 @@ async function browserLogin() {
590
590
  }
591
591
  }
592
592
  function questionHidden(prompt, rl) {
593
- return new Promise((resolve5) => {
593
+ return new Promise((resolve6) => {
594
594
  const stdin = process.stdin;
595
595
  const stdout = process.stdout;
596
596
  stdout.write(prompt);
@@ -610,7 +610,7 @@ function questionHidden(prompt, rl) {
610
610
  }
611
611
  stdin.removeListener("data", onData);
612
612
  stdout.write("\n");
613
- resolve5(password);
613
+ resolve6(password);
614
614
  break;
615
615
  case "":
616
616
  process.exit(1);
@@ -682,7 +682,7 @@ var whoamiCommand = new Command3("whoami").description("Show current logged in u
682
682
  });
683
683
 
684
684
  // src/commands/spine/index.ts
685
- import { Command as Command8 } from "commander";
685
+ import { Command as Command9 } from "commander";
686
686
 
687
687
  // src/commands/spine/validate.ts
688
688
  import { Command as Command4 } from "commander";
@@ -1937,26 +1937,279 @@ function pascalCase2(str) {
1937
1937
  return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
1938
1938
  }
1939
1939
 
1940
+ // src/commands/spine/watch.ts
1941
+ import { Command as Command8 } from "commander";
1942
+ import * as fs5 from "fs";
1943
+ import * as path6 from "path";
1944
+ import ora6 from "ora";
1945
+ var watchCommand = new Command8("watch").description("Watch directory for new Spine exports and auto-upload").argument("<directory>", "Directory to watch for Spine files").option("-s, --studio <studio>", "Studio ID or slug (uses default if set)").option("-g, --game <game>", "Game ID or slug (uses default if set)").option("--batch", "Enable batch mode for multiple skeletons sharing atlas").option("--interval <seconds>", "Check interval in seconds", "2").action(async (directory, options) => {
1946
+ if (!credentials.isLoggedIn()) {
1947
+ logger.error("Not logged in.");
1948
+ logger.info(`Run ${logger.command("playtagon login")} first.`);
1949
+ process.exit(1);
1950
+ }
1951
+ const studioOption = options.studio || config.defaultStudio;
1952
+ const gameOption = options.game || config.defaultGame;
1953
+ if (!studioOption) {
1954
+ logger.error("Studio is required.");
1955
+ logger.info(`Either provide ${logger.command("--studio <slug>")} or set a default:`);
1956
+ logger.info(` ${logger.command("playtagon config --studio <slug>")}`);
1957
+ process.exit(1);
1958
+ }
1959
+ const absolutePath = path6.resolve(directory);
1960
+ if (!fs5.existsSync(absolutePath)) {
1961
+ logger.error(`Directory not found: ${absolutePath}`);
1962
+ process.exit(1);
1963
+ }
1964
+ const spinner = ora6("Resolving studio...").start();
1965
+ const studio = await resolveStudio(studioOption);
1966
+ if (!studio) {
1967
+ spinner.fail("Studio not found");
1968
+ logger.error(`Studio "${studioOption}" not found or you don't have access.`);
1969
+ process.exit(1);
1970
+ }
1971
+ spinner.succeed(`Studio: ${studio.name}`);
1972
+ let game = null;
1973
+ if (gameOption) {
1974
+ spinner.start("Resolving game...");
1975
+ game = await resolveGame(studio.id, gameOption);
1976
+ if (!game) {
1977
+ spinner.fail("Game not found");
1978
+ logger.error(`Game "${gameOption}" not found in studio "${studio.name}".`);
1979
+ process.exit(1);
1980
+ }
1981
+ spinner.succeed(`Game: ${game.name}`);
1982
+ }
1983
+ const state = {
1984
+ lastModified: /* @__PURE__ */ new Map(),
1985
+ isUploading: false,
1986
+ studioId: studio.id,
1987
+ gameId: game?.id
1988
+ };
1989
+ await scanDirectory(absolutePath, state);
1990
+ console.log();
1991
+ logger.header("Watching for Spine Exports");
1992
+ logger.item("Directory", absolutePath);
1993
+ logger.item("Studio", studio.name);
1994
+ if (game) logger.item("Game", game.name);
1995
+ logger.item("Batch Mode", options.batch ? "Yes" : "No");
1996
+ logger.item("Check Interval", `${options.interval}s`);
1997
+ console.log();
1998
+ logger.info("Watching for changes... (Press Ctrl+C to stop)");
1999
+ console.log();
2000
+ const intervalMs = parseInt(options.interval) * 1e3;
2001
+ setInterval(async () => {
2002
+ await checkForChanges(absolutePath, state, options.batch);
2003
+ }, intervalMs);
2004
+ process.on("SIGINT", () => {
2005
+ console.log();
2006
+ logger.success("Stopped watching.");
2007
+ process.exit(0);
2008
+ });
2009
+ });
2010
+ async function scanDirectory(directory, state) {
2011
+ const files = getAllFiles(directory);
2012
+ for (const file of files) {
2013
+ const stats = fs5.statSync(file);
2014
+ state.lastModified.set(file, stats.mtimeMs);
2015
+ }
2016
+ }
2017
+ function getAllFiles(directory) {
2018
+ const files = [];
2019
+ try {
2020
+ const entries = fs5.readdirSync(directory, { withFileTypes: true });
2021
+ for (const entry of entries) {
2022
+ const fullPath = path6.join(directory, entry.name);
2023
+ if (entry.isFile()) {
2024
+ const ext = path6.extname(entry.name).toLowerCase();
2025
+ if ([".json", ".skel", ".atlas", ".png", ".jpg", ".jpeg", ".webp"].includes(ext)) {
2026
+ files.push(fullPath);
2027
+ }
2028
+ } else if (entry.isDirectory()) {
2029
+ files.push(...getAllFiles(fullPath));
2030
+ }
2031
+ }
2032
+ } catch (error) {
2033
+ }
2034
+ return files;
2035
+ }
2036
+ async function checkForChanges(directory, state, batchMode) {
2037
+ if (state.isUploading) {
2038
+ return;
2039
+ }
2040
+ const files = getAllFiles(directory);
2041
+ let hasChanges = false;
2042
+ let newestChangeTime = 0;
2043
+ for (const file of files) {
2044
+ try {
2045
+ const stats = fs5.statSync(file);
2046
+ const lastModified = state.lastModified.get(file) || 0;
2047
+ if (stats.mtimeMs > lastModified) {
2048
+ hasChanges = true;
2049
+ newestChangeTime = Math.max(newestChangeTime, stats.mtimeMs);
2050
+ }
2051
+ } catch (error) {
2052
+ }
2053
+ }
2054
+ if (!hasChanges) {
2055
+ return;
2056
+ }
2057
+ const timeSinceLastChange = Date.now() - newestChangeTime;
2058
+ if (timeSinceLastChange < 3e3) {
2059
+ return;
2060
+ }
2061
+ const hasAtlas = files.some((f) => f.endsWith(".atlas"));
2062
+ const hasTextures = files.some((f) => /\.(png|jpg|jpeg|webp)$/i.test(f));
2063
+ const hasSkeletons = files.some((f) => f.endsWith(".json") || f.endsWith(".skel"));
2064
+ if (!hasAtlas || !hasTextures || !hasSkeletons) {
2065
+ return;
2066
+ }
2067
+ for (const file of files) {
2068
+ try {
2069
+ const stats = fs5.statSync(file);
2070
+ state.lastModified.set(file, stats.mtimeMs);
2071
+ } catch (error) {
2072
+ }
2073
+ }
2074
+ state.isUploading = true;
2075
+ await uploadDirectory(directory, state, batchMode);
2076
+ state.isUploading = false;
2077
+ }
2078
+ async function uploadDirectory(directory, state, batchMode) {
2079
+ const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
2080
+ console.log(`[${timestamp}] Changes detected, uploading...`);
2081
+ const spinner = ora6("Validating files...").start();
2082
+ let validation;
2083
+ try {
2084
+ validation = validateDirectory(directory, batchMode);
2085
+ } catch (error) {
2086
+ spinner.fail("Validation failed");
2087
+ logger.error(error instanceof Error ? error.message : "Unknown error");
2088
+ console.log();
2089
+ return;
2090
+ }
2091
+ if (!validation.isValid) {
2092
+ spinner.fail("Validation failed");
2093
+ const errors = validation.issues.filter((i) => i.type === "error");
2094
+ for (const error of errors) {
2095
+ logger.validationError(
2096
+ error.file ? `${error.file}: ${error.message}` : error.message
2097
+ );
2098
+ }
2099
+ console.log();
2100
+ return;
2101
+ }
2102
+ spinner.text = `Validated ${validation.assets.length} asset(s)`;
2103
+ spinner.succeed();
2104
+ const assetName = path6.basename(directory);
2105
+ const slug = generateSlug(assetName);
2106
+ if (!validateSlug(slug)) {
2107
+ logger.error(`Invalid slug generated: "${slug}". Skipping upload.`);
2108
+ console.log();
2109
+ return;
2110
+ }
2111
+ spinner.start("Uploading...");
2112
+ try {
2113
+ const formData = await buildFormData2(validation, {
2114
+ studioId: state.studioId,
2115
+ gameId: state.gameId,
2116
+ name: assetName,
2117
+ slug,
2118
+ batchMode: validation.isBatchMode
2119
+ });
2120
+ const result = await uploadSpineAsset(formData);
2121
+ if (result.success) {
2122
+ spinner.succeed("Upload complete!");
2123
+ if (result.assets && result.assets.length > 1) {
2124
+ console.log(` Uploaded ${result.assets.length} assets (batch mode)`);
2125
+ for (const asset of result.assets) {
2126
+ console.log(` ${asset.name} (${asset.slug}) - ${asset.status}`);
2127
+ }
2128
+ } else if (result.asset) {
2129
+ console.log(` ${result.asset.name} (${result.asset.slug}) - ${result.asset.status}`);
2130
+ }
2131
+ console.log();
2132
+ } else {
2133
+ spinner.fail("Upload failed");
2134
+ logger.error(result.error || "Unknown error");
2135
+ console.log();
2136
+ }
2137
+ } catch (error) {
2138
+ spinner.fail("Upload failed");
2139
+ logger.error(error instanceof Error ? error.message : "Unknown error");
2140
+ console.log();
2141
+ }
2142
+ }
2143
+ async function buildFormData2(validation, options) {
2144
+ const formData = new FormData();
2145
+ formData.append("studioId", options.studioId);
2146
+ if (options.gameId) formData.append("gameId", options.gameId);
2147
+ formData.append("name", options.name);
2148
+ formData.append("slug", options.slug);
2149
+ if (options.batchMode) formData.append("batchMode", "true");
2150
+ const filesAdded = /* @__PURE__ */ new Set();
2151
+ for (const asset of validation.assets) {
2152
+ const filePath = asset.skeleton.path;
2153
+ if (!filesAdded.has(filePath)) {
2154
+ const buffer = fs5.readFileSync(filePath);
2155
+ const blob = new Blob([buffer]);
2156
+ formData.append("files", blob, getBaseName(filePath));
2157
+ filesAdded.add(filePath);
2158
+ }
2159
+ }
2160
+ if (validation.sharedAtlas && !filesAdded.has(validation.sharedAtlas)) {
2161
+ const buffer = fs5.readFileSync(validation.sharedAtlas);
2162
+ const blob = new Blob([buffer]);
2163
+ formData.append("files", blob, getBaseName(validation.sharedAtlas));
2164
+ filesAdded.add(validation.sharedAtlas);
2165
+ }
2166
+ for (const texturePath of validation.sharedTextures) {
2167
+ if (!filesAdded.has(texturePath)) {
2168
+ const buffer = fs5.readFileSync(texturePath);
2169
+ const blob = new Blob([buffer]);
2170
+ formData.append("files", blob, getBaseName(texturePath));
2171
+ filesAdded.add(texturePath);
2172
+ }
2173
+ }
2174
+ for (const asset of validation.assets) {
2175
+ if (asset.atlasPath && !filesAdded.has(asset.atlasPath)) {
2176
+ const buffer = fs5.readFileSync(asset.atlasPath);
2177
+ const blob = new Blob([buffer]);
2178
+ formData.append("files", blob, getBaseName(asset.atlasPath));
2179
+ filesAdded.add(asset.atlasPath);
2180
+ }
2181
+ for (const texturePath of asset.texturePaths) {
2182
+ if (!filesAdded.has(texturePath)) {
2183
+ const buffer = fs5.readFileSync(texturePath);
2184
+ const blob = new Blob([buffer]);
2185
+ formData.append("files", blob, getBaseName(texturePath));
2186
+ filesAdded.add(texturePath);
2187
+ }
2188
+ }
2189
+ }
2190
+ return formData;
2191
+ }
2192
+
1940
2193
  // src/commands/spine/index.ts
1941
- var spineCommand = new Command8("spine").description("Manage Spine animation assets").addCommand(validateCommand).addCommand(uploadCommand).addCommand(presetCommand).addCommand(syncCommand);
2194
+ var spineCommand = new Command9("spine").description("Manage Spine animation assets").addCommand(validateCommand).addCommand(uploadCommand).addCommand(watchCommand).addCommand(presetCommand).addCommand(syncCommand);
1942
2195
 
1943
2196
  // src/commands/setup.ts
1944
- import { Command as Command9 } from "commander";
1945
- import * as fs5 from "fs";
1946
- import * as path6 from "path";
2197
+ import { Command as Command10 } from "commander";
2198
+ import * as fs6 from "fs";
2199
+ import * as path7 from "path";
1947
2200
  import * as os from "os";
1948
2201
  import { execFile } from "child_process";
1949
- import ora6 from "ora";
1950
- var PLAYTAGON_DIR = path6.join(os.homedir(), ".playtagon");
1951
- var setupCommand = new Command9("setup").description("Set up integrations").addCommand(spineIntegrationCommand());
2202
+ import ora7 from "ora";
2203
+ var PLAYTAGON_DIR = path7.join(os.homedir(), ".playtagon");
2204
+ var setupCommand = new Command10("setup").description("Set up integrations").addCommand(spineIntegrationCommand());
1952
2205
  function spineIntegrationCommand() {
1953
- return new Command9("spine-integration").description("Set up automatic Spine Editor to Platform upload").option("-s, --studio <studio>", "Default studio for uploads").option("-g, --game <game>", "Default game for uploads").option("--force", "Overwrite existing setup files").action(async (options) => {
2206
+ return new Command10("spine-integration").description("Set up automatic Spine Editor to Platform upload").option("-s, --studio <studio>", "Default studio for uploads").option("-g, --game <game>", "Default game for uploads").option("--force", "Overwrite existing setup files").action(async (options) => {
1954
2207
  if (!credentials.isLoggedIn()) {
1955
2208
  logger.error("Not logged in.");
1956
2209
  logger.info(`Run ${logger.command("playtagon login")} first.`);
1957
2210
  process.exit(1);
1958
2211
  }
1959
- const spinner = ora6("Checking authentication...").start();
2212
+ const spinner = ora7("Checking authentication...").start();
1960
2213
  const user = await getCurrentUser();
1961
2214
  if (!user) {
1962
2215
  spinner.fail("Session expired");
@@ -1988,8 +2241,8 @@ function spineIntegrationCommand() {
1988
2241
  }
1989
2242
  }
1990
2243
  spinner.start("Creating setup files...");
1991
- if (!fs5.existsSync(PLAYTAGON_DIR)) {
1992
- fs5.mkdirSync(PLAYTAGON_DIR, { recursive: true, mode: 448 });
2244
+ if (!fs6.existsSync(PLAYTAGON_DIR)) {
2245
+ fs6.mkdirSync(PLAYTAGON_DIR, { recursive: true, mode: 448 });
1993
2246
  }
1994
2247
  const scriptPaths = generatePostExportScripts(studioSlug, options.game, options.force);
1995
2248
  const presetPath = generateExportPreset(scriptPaths.sh, options.force);
@@ -2058,8 +2311,8 @@ function spineIntegrationCommand() {
2058
2311
  });
2059
2312
  }
2060
2313
  function generatePostExportScripts(studioSlug, gameSlug, force = false) {
2061
- const shPath = path6.join(PLAYTAGON_DIR, "upload.sh");
2062
- const batPath = path6.join(PLAYTAGON_DIR, "upload.bat");
2314
+ const shPath = path7.join(PLAYTAGON_DIR, "upload.sh");
2315
+ const batPath = path7.join(PLAYTAGON_DIR, "upload.bat");
2063
2316
  let uploadCmd = `playtagon spine upload "$1" --studio ${studioSlug}`;
2064
2317
  if (gameSlug) {
2065
2318
  uploadCmd += ` --game ${gameSlug}`;
@@ -2132,25 +2385,25 @@ if %ERRORLEVEL% EQU 0 (
2132
2385
  powershell -Command "& {Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('Upload failed! Check log file', 'Playtagon Error', 'OK', 'Error')}" 2>nul
2133
2386
  )
2134
2387
  `;
2135
- if (!fs5.existsSync(shPath) || force) {
2136
- fs5.writeFileSync(shPath, shScript, { mode: 493 });
2388
+ if (!fs6.existsSync(shPath) || force) {
2389
+ fs6.writeFileSync(shPath, shScript, { mode: 493 });
2137
2390
  } else {
2138
2391
  logger.warn(`${shPath} already exists. Use --force to overwrite.`);
2139
2392
  }
2140
- if (!fs5.existsSync(batPath) || force) {
2141
- fs5.writeFileSync(batPath, batScript);
2393
+ if (!fs6.existsSync(batPath) || force) {
2394
+ fs6.writeFileSync(batPath, batScript);
2142
2395
  }
2143
2396
  return { sh: shPath, bat: batPath };
2144
2397
  }
2145
2398
  function generateExportPreset(scriptPath, force = false) {
2146
- const presetPath = path6.join(PLAYTAGON_DIR, "playtagon-spine-preset.export.json");
2399
+ const presetPath = path7.join(PLAYTAGON_DIR, "playtagon-spine-preset.export.json");
2147
2400
  const preset = {
2148
2401
  ...SPINE_EXPORT_PRESET,
2149
2402
  name: "Upload to Playtagon",
2150
2403
  postScript: process.platform === "win32" ? `"${scriptPath.replace(".sh", ".bat")}" "{output}"` : `"${scriptPath}" "{output}"`
2151
2404
  };
2152
- if (!fs5.existsSync(presetPath) || force) {
2153
- fs5.writeFileSync(presetPath, JSON.stringify(preset, null, 2));
2405
+ if (!fs6.existsSync(presetPath) || force) {
2406
+ fs6.writeFileSync(presetPath, JSON.stringify(preset, null, 2));
2154
2407
  } else {
2155
2408
  logger.warn(`${presetPath} already exists. Use --force to overwrite.`);
2156
2409
  }
@@ -2178,9 +2431,9 @@ function openFolder(folderPath) {
2178
2431
  }
2179
2432
 
2180
2433
  // src/commands/config.ts
2181
- import { Command as Command10 } from "commander";
2182
- import ora7 from "ora";
2183
- var configCommand = new Command10("config").description("View or set CLI configuration").option("-s, --studio <studio>", "Set default studio").option("-g, --game <game>", "Set default game").option("--clear", "Clear all default settings").action(async (options) => {
2434
+ import { Command as Command11 } from "commander";
2435
+ import ora8 from "ora";
2436
+ var configCommand = new Command11("config").description("View or set CLI configuration").option("-s, --studio <studio>", "Set default studio").option("-g, --game <game>", "Set default game").option("--clear", "Clear all default settings").action(async (options) => {
2184
2437
  if (options.clear) {
2185
2438
  config.defaultStudio = void 0;
2186
2439
  config.defaultGame = void 0;
@@ -2193,7 +2446,7 @@ var configCommand = new Command10("config").description("View or set CLI configu
2193
2446
  logger.info(`Run ${logger.command("playtagon login")} first.`);
2194
2447
  process.exit(1);
2195
2448
  }
2196
- const spinner = ora7("Verifying studio...").start();
2449
+ const spinner = ora8("Verifying studio...").start();
2197
2450
  const studios = await getStudios();
2198
2451
  const studio = studios.find(
2199
2452
  (s) => s.slug === options.studio || s.id === options.studio || s.name === options.studio
@@ -2223,7 +2476,7 @@ var configCommand = new Command10("config").description("View or set CLI configu
2223
2476
  logger.info(`Either provide ${logger.command("--studio <slug>")} or set a default studio first.`);
2224
2477
  process.exit(1);
2225
2478
  }
2226
- const spinner = ora7("Verifying game...").start();
2479
+ const spinner = ora8("Verifying game...").start();
2227
2480
  const studios = await getStudios();
2228
2481
  const studio = studios.find(
2229
2482
  (s) => s.slug === studioSlug || s.id === studioSlug || s.name === studioSlug
@@ -2283,7 +2536,7 @@ var configCommand = new Command10("config").description("View or set CLI configu
2283
2536
  });
2284
2537
 
2285
2538
  // src/index.ts
2286
- var program = new Command11();
2539
+ var program = new Command12();
2287
2540
  program.name("playtagon").description("Playtagon CLI - Upload and manage game assets").version("0.3.0");
2288
2541
  program.addCommand(loginCommand);
2289
2542
  program.addCommand(logoutCommand);