@playtagon/cli 0.1.0 → 0.2.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/index.js +250 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,10 +6,13 @@ import { Command as Command10 } from "commander";
|
|
|
6
6
|
// src/commands/login.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import * as readline from "readline";
|
|
9
|
+
import open from "open";
|
|
9
10
|
import ora from "ora";
|
|
10
11
|
|
|
11
12
|
// src/lib/auth.ts
|
|
12
13
|
import { createClient } from "@supabase/supabase-js";
|
|
14
|
+
import * as http from "http";
|
|
15
|
+
import * as crypto from "crypto";
|
|
13
16
|
|
|
14
17
|
// src/lib/config.ts
|
|
15
18
|
import Conf from "conf";
|
|
@@ -156,6 +159,18 @@ var logger = {
|
|
|
156
159
|
|
|
157
160
|
// src/lib/auth.ts
|
|
158
161
|
var supabaseClient = null;
|
|
162
|
+
function base64URLEncode(buffer) {
|
|
163
|
+
return buffer.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
164
|
+
}
|
|
165
|
+
function sha256(buffer) {
|
|
166
|
+
return crypto.createHash("sha256").update(buffer).digest();
|
|
167
|
+
}
|
|
168
|
+
function generateCodeVerifier() {
|
|
169
|
+
return base64URLEncode(crypto.randomBytes(32));
|
|
170
|
+
}
|
|
171
|
+
function generateCodeChallenge(verifier) {
|
|
172
|
+
return base64URLEncode(sha256(verifier));
|
|
173
|
+
}
|
|
159
174
|
function getSupabaseClient() {
|
|
160
175
|
if (!supabaseClient) {
|
|
161
176
|
supabaseClient = createClient(config.supabaseUrl, config.supabaseAnonKey, {
|
|
@@ -227,6 +242,203 @@ async function loginWithEmail(email, password) {
|
|
|
227
242
|
email: data.user?.email
|
|
228
243
|
});
|
|
229
244
|
}
|
|
245
|
+
async function loginWithBrowser() {
|
|
246
|
+
return new Promise((resolve4) => {
|
|
247
|
+
const port = 54321;
|
|
248
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
249
|
+
const codeVerifier = generateCodeVerifier();
|
|
250
|
+
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
251
|
+
const authUrl = new URL(`${config.supabaseUrl}/auth/v1/authorize`);
|
|
252
|
+
authUrl.searchParams.set("provider", "google");
|
|
253
|
+
authUrl.searchParams.set("redirect_to", redirectUri);
|
|
254
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
255
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
256
|
+
const loginUrl = new URL(`${config.supabaseUrl}/auth/v1/authorize`);
|
|
257
|
+
const platformAuthUrl = `https://platform.playtagon.com/cli-auth?redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${codeChallenge}`;
|
|
258
|
+
let server = null;
|
|
259
|
+
let timeoutId = null;
|
|
260
|
+
const cleanup = () => {
|
|
261
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
262
|
+
if (server) {
|
|
263
|
+
server.close();
|
|
264
|
+
server = null;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
server = http.createServer(async (req, res) => {
|
|
268
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
269
|
+
if (url.pathname === "/callback") {
|
|
270
|
+
const code = url.searchParams.get("code");
|
|
271
|
+
const error = url.searchParams.get("error");
|
|
272
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
273
|
+
if (error) {
|
|
274
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
275
|
+
res.end(getErrorHtml(errorDescription || error));
|
|
276
|
+
cleanup();
|
|
277
|
+
resolve4({ success: false, error: errorDescription || error });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (code) {
|
|
281
|
+
try {
|
|
282
|
+
const client = getSupabaseClient();
|
|
283
|
+
const { data, error: exchangeError } = await client.auth.exchangeCodeForSession(code);
|
|
284
|
+
if (exchangeError || !data.session) {
|
|
285
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
286
|
+
res.end(getErrorHtml(exchangeError?.message || "Failed to exchange code"));
|
|
287
|
+
cleanup();
|
|
288
|
+
resolve4({ success: false, error: exchangeError?.message || "Failed to exchange code" });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
credentials.save({
|
|
292
|
+
accessToken: data.session.access_token,
|
|
293
|
+
refreshToken: data.session.refresh_token,
|
|
294
|
+
expiresAt: data.session.expires_at ? data.session.expires_at * 1e3 : Date.now() + 3600 * 1e3,
|
|
295
|
+
userId: data.user?.id,
|
|
296
|
+
email: data.user?.email
|
|
297
|
+
});
|
|
298
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
299
|
+
res.end(getSuccessHtml(data.user?.email || ""));
|
|
300
|
+
cleanup();
|
|
301
|
+
resolve4({ success: true });
|
|
302
|
+
} catch (err) {
|
|
303
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
304
|
+
res.end(getErrorHtml(err instanceof Error ? err.message : "Unknown error"));
|
|
305
|
+
cleanup();
|
|
306
|
+
resolve4({ success: false, error: err instanceof Error ? err.message : "Unknown error" });
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const accessToken = url.searchParams.get("access_token");
|
|
311
|
+
const refreshToken = url.searchParams.get("refresh_token");
|
|
312
|
+
if (accessToken) {
|
|
313
|
+
credentials.save({
|
|
314
|
+
accessToken,
|
|
315
|
+
refreshToken: refreshToken || void 0,
|
|
316
|
+
expiresAt: Date.now() + 3600 * 1e3
|
|
317
|
+
});
|
|
318
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
319
|
+
res.end(getSuccessHtml(""));
|
|
320
|
+
cleanup();
|
|
321
|
+
resolve4({ success: true });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
325
|
+
res.end(getErrorHtml("No authorization code received"));
|
|
326
|
+
cleanup();
|
|
327
|
+
resolve4({ success: false, error: "No authorization code received" });
|
|
328
|
+
} else {
|
|
329
|
+
res.writeHead(404);
|
|
330
|
+
res.end("Not found");
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
server.listen(port, () => {
|
|
334
|
+
logger.info(`Waiting for authentication on port ${port}...`);
|
|
335
|
+
});
|
|
336
|
+
server.on("error", (err) => {
|
|
337
|
+
if (err.code === "EADDRINUSE") {
|
|
338
|
+
cleanup();
|
|
339
|
+
resolve4({ success: false, error: `Port ${port} is already in use. Please close any applications using it.` });
|
|
340
|
+
} else {
|
|
341
|
+
cleanup();
|
|
342
|
+
resolve4({ success: false, error: err.message });
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
timeoutId = setTimeout(() => {
|
|
346
|
+
cleanup();
|
|
347
|
+
resolve4({ success: false, error: "Authentication timed out. Please try again." });
|
|
348
|
+
}, 5 * 60 * 1e3);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
function getAuthUrl() {
|
|
352
|
+
const port = 54321;
|
|
353
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
354
|
+
return `https://platform.playtagon.com/cli-auth?redirect_uri=${encodeURIComponent(redirectUri)}`;
|
|
355
|
+
}
|
|
356
|
+
function getSuccessHtml(email) {
|
|
357
|
+
return `
|
|
358
|
+
<!DOCTYPE html>
|
|
359
|
+
<html>
|
|
360
|
+
<head>
|
|
361
|
+
<title>Login Successful - Playtagon CLI</title>
|
|
362
|
+
<style>
|
|
363
|
+
body {
|
|
364
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
365
|
+
display: flex;
|
|
366
|
+
justify-content: center;
|
|
367
|
+
align-items: center;
|
|
368
|
+
min-height: 100vh;
|
|
369
|
+
margin: 0;
|
|
370
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
371
|
+
color: #fff;
|
|
372
|
+
}
|
|
373
|
+
.container {
|
|
374
|
+
text-align: center;
|
|
375
|
+
padding: 40px;
|
|
376
|
+
background: rgba(255,255,255,0.1);
|
|
377
|
+
border-radius: 16px;
|
|
378
|
+
backdrop-filter: blur(10px);
|
|
379
|
+
}
|
|
380
|
+
.checkmark {
|
|
381
|
+
font-size: 64px;
|
|
382
|
+
margin-bottom: 20px;
|
|
383
|
+
}
|
|
384
|
+
h1 { margin: 0 0 10px; font-size: 24px; }
|
|
385
|
+
p { margin: 0; opacity: 0.8; }
|
|
386
|
+
.email { font-weight: bold; color: #4ade80; }
|
|
387
|
+
</style>
|
|
388
|
+
</head>
|
|
389
|
+
<body>
|
|
390
|
+
<div class="container">
|
|
391
|
+
<div class="checkmark">\u2713</div>
|
|
392
|
+
<h1>Login Successful!</h1>
|
|
393
|
+
<p>You're now logged in${email ? ` as <span class="email">${email}</span>` : ""}.</p>
|
|
394
|
+
<p style="margin-top: 20px; opacity: 0.6;">You can close this window and return to your terminal.</p>
|
|
395
|
+
</div>
|
|
396
|
+
</body>
|
|
397
|
+
</html>`;
|
|
398
|
+
}
|
|
399
|
+
function getErrorHtml(error) {
|
|
400
|
+
return `
|
|
401
|
+
<!DOCTYPE html>
|
|
402
|
+
<html>
|
|
403
|
+
<head>
|
|
404
|
+
<title>Login Failed - Playtagon CLI</title>
|
|
405
|
+
<style>
|
|
406
|
+
body {
|
|
407
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
408
|
+
display: flex;
|
|
409
|
+
justify-content: center;
|
|
410
|
+
align-items: center;
|
|
411
|
+
min-height: 100vh;
|
|
412
|
+
margin: 0;
|
|
413
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
414
|
+
color: #fff;
|
|
415
|
+
}
|
|
416
|
+
.container {
|
|
417
|
+
text-align: center;
|
|
418
|
+
padding: 40px;
|
|
419
|
+
background: rgba(255,255,255,0.1);
|
|
420
|
+
border-radius: 16px;
|
|
421
|
+
backdrop-filter: blur(10px);
|
|
422
|
+
}
|
|
423
|
+
.icon {
|
|
424
|
+
font-size: 64px;
|
|
425
|
+
margin-bottom: 20px;
|
|
426
|
+
}
|
|
427
|
+
h1 { margin: 0 0 10px; font-size: 24px; color: #f87171; }
|
|
428
|
+
p { margin: 0; opacity: 0.8; }
|
|
429
|
+
.error { color: #fca5a5; font-family: monospace; }
|
|
430
|
+
</style>
|
|
431
|
+
</head>
|
|
432
|
+
<body>
|
|
433
|
+
<div class="container">
|
|
434
|
+
<div class="icon">\u2717</div>
|
|
435
|
+
<h1>Login Failed</h1>
|
|
436
|
+
<p class="error">${error}</p>
|
|
437
|
+
<p style="margin-top: 20px; opacity: 0.6;">Please return to your terminal and try again.</p>
|
|
438
|
+
</div>
|
|
439
|
+
</body>
|
|
440
|
+
</html>`;
|
|
441
|
+
}
|
|
230
442
|
function logout() {
|
|
231
443
|
credentials.clear();
|
|
232
444
|
}
|
|
@@ -262,7 +474,7 @@ function getAccessToken() {
|
|
|
262
474
|
}
|
|
263
475
|
|
|
264
476
|
// src/commands/login.ts
|
|
265
|
-
var loginCommand = new Command("login").description("Authenticate with Playtagon").option("-e, --email <email>", "Login with email").option("-
|
|
477
|
+
var loginCommand = new Command("login").description("Authenticate with Playtagon").option("-e, --email <email>", "Login with email/password instead of browser").option("--no-browser", "Use email/password login instead of browser").action(async (options) => {
|
|
266
478
|
if (credentials.isLoggedIn()) {
|
|
267
479
|
const user = await getCurrentUser();
|
|
268
480
|
if (user) {
|
|
@@ -271,12 +483,12 @@ var loginCommand = new Command("login").description("Authenticate with Playtagon
|
|
|
271
483
|
return;
|
|
272
484
|
}
|
|
273
485
|
}
|
|
274
|
-
if (options.
|
|
275
|
-
await browserLogin();
|
|
276
|
-
} else if (options.email) {
|
|
486
|
+
if (options.email) {
|
|
277
487
|
await emailLogin(options.email);
|
|
278
|
-
} else {
|
|
488
|
+
} else if (options.browser === false) {
|
|
279
489
|
await promptEmailLogin();
|
|
490
|
+
} else {
|
|
491
|
+
await browserLogin();
|
|
280
492
|
}
|
|
281
493
|
});
|
|
282
494
|
async function promptEmailLogin() {
|
|
@@ -340,25 +552,41 @@ async function emailLogin(email) {
|
|
|
340
552
|
}
|
|
341
553
|
}
|
|
342
554
|
async function browserLogin() {
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
555
|
+
const authUrl = getAuthUrl();
|
|
556
|
+
console.log();
|
|
557
|
+
logger.info("Opening browser to log in...");
|
|
558
|
+
logger.info("");
|
|
559
|
+
logger.info(`If browser doesn't open, visit:`);
|
|
560
|
+
logger.info(` ${logger.link(authUrl)}`);
|
|
561
|
+
console.log();
|
|
562
|
+
try {
|
|
563
|
+
await open(authUrl);
|
|
564
|
+
} catch {
|
|
565
|
+
}
|
|
566
|
+
const spinner = ora("Waiting for authentication...").start();
|
|
567
|
+
const result = await loginWithBrowser();
|
|
568
|
+
if (result.success) {
|
|
569
|
+
spinner.succeed("Logged in successfully!");
|
|
570
|
+
const user = await getCurrentUser();
|
|
571
|
+
if (user) {
|
|
572
|
+
console.log();
|
|
573
|
+
logger.header("Account");
|
|
574
|
+
logger.item("Email", user.email);
|
|
575
|
+
if (user.studios.length > 0) {
|
|
576
|
+
logger.item(
|
|
577
|
+
"Studios",
|
|
578
|
+
user.studios.map((s) => s.name).join(", ")
|
|
579
|
+
);
|
|
580
|
+
}
|
|
350
581
|
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
logger.error(
|
|
354
|
-
|
|
355
|
-
logger.info("
|
|
356
|
-
logger.info(` ${logger.command("playtagon login -
|
|
582
|
+
} else {
|
|
583
|
+
spinner.fail("Login failed");
|
|
584
|
+
logger.error(result.error || "Unknown error");
|
|
585
|
+
console.log();
|
|
586
|
+
logger.info("You can also try email/password login:");
|
|
587
|
+
logger.info(` ${logger.command("playtagon login --no-browser")}`);
|
|
357
588
|
process.exit(1);
|
|
358
589
|
}
|
|
359
|
-
logger.info("Browser-based login is not yet implemented.");
|
|
360
|
-
logger.info("Please use email login:");
|
|
361
|
-
logger.info(` ${logger.command("playtagon login")}`);
|
|
362
590
|
}
|
|
363
591
|
function questionHidden(prompt, rl) {
|
|
364
592
|
return new Promise((resolve4) => {
|
|
@@ -1588,7 +1816,7 @@ var configCommand = new Command9("config").description("View or set CLI configur
|
|
|
1588
1816
|
|
|
1589
1817
|
// src/index.ts
|
|
1590
1818
|
var program = new Command10();
|
|
1591
|
-
program.name("playtagon").description("Playtagon CLI - Upload and manage game assets").version("0.
|
|
1819
|
+
program.name("playtagon").description("Playtagon CLI - Upload and manage game assets").version("0.2.0");
|
|
1592
1820
|
program.addCommand(loginCommand);
|
|
1593
1821
|
program.addCommand(logoutCommand);
|
|
1594
1822
|
program.addCommand(whoamiCommand);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/auth.ts","../src/lib/config.ts","../src/utils/logger.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/spine/index.ts","../src/commands/spine/validate.ts","../src/lib/spine-validator.ts","../src/utils/files.ts","../src/commands/spine/upload.ts","../src/lib/api.ts","../src/commands/spine/preset.ts","../src/commands/setup.ts","../src/commands/config.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { whoamiCommand } from './commands/whoami.js';\nimport { spineCommand } from './commands/spine/index.js';\nimport { setupCommand } from './commands/setup.js';\nimport { configCommand } from './commands/config.js';\n\nconst program = new Command();\n\nprogram\n .name('playtagon')\n .description('Playtagon CLI - Upload and manage game assets')\n .version('0.1.0');\n\n// Add commands\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\nprogram.addCommand(spineCommand);\nprogram.addCommand(setupCommand);\nprogram.addCommand(configCommand);\n\n// Parse arguments\nprogram.parse();\n","import { Command } from 'commander';\nimport * as readline from 'node:readline';\nimport open from 'open';\nimport ora from 'ora';\nimport { loginWithEmail, getCurrentUser, getSupabaseClient } from '../lib/auth.js';\nimport { credentials } from '../lib/config.js';\nimport { logger } from '../utils/logger.js';\n\nexport const loginCommand = new Command('login')\n .description('Authenticate with Playtagon')\n .option('-e, --email <email>', 'Login with email')\n .option('-b, --browser', 'Open browser for OAuth login')\n .action(async (options) => {\n // Check if already logged in\n if (credentials.isLoggedIn()) {\n const user = await getCurrentUser();\n if (user) {\n logger.info(`Already logged in as ${logger.value(user.email)}`);\n logger.info(`Run ${logger.command('playtagon logout')} to sign out.`);\n return;\n }\n }\n\n if (options.browser) {\n await browserLogin();\n } else if (options.email) {\n await emailLogin(options.email);\n } else {\n // Default: prompt for email login\n await promptEmailLogin();\n }\n });\n\nasync function promptEmailLogin(): Promise<void> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const question = (prompt: string): Promise<string> =>\n new Promise((resolve) => {\n rl.question(prompt, resolve);\n });\n\n try {\n const email = await question('Email: ');\n const password = await questionHidden('Password: ', rl);\n\n const spinner = ora('Logging in...').start();\n\n try {\n await loginWithEmail(email, password);\n spinner.succeed('Logged in successfully!');\n\n const user = await getCurrentUser();\n if (user) {\n logger.header('Account');\n logger.item('Email', user.email);\n if (user.studios.length > 0) {\n logger.item(\n 'Studios',\n user.studios.map((s) => s.name).join(', ')\n );\n }\n }\n } catch (error) {\n spinner.fail('Login failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n } finally {\n rl.close();\n }\n}\n\nasync function emailLogin(email: string): Promise<void> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const password = await questionHidden('Password: ', rl);\n\n const spinner = ora('Logging in...').start();\n\n try {\n await loginWithEmail(email, password);\n spinner.succeed('Logged in successfully!');\n\n const user = await getCurrentUser();\n if (user) {\n logger.header('Account');\n logger.item('Email', user.email);\n }\n } catch (error) {\n spinner.fail('Login failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n } finally {\n rl.close();\n }\n}\n\nasync function browserLogin(): Promise<void> {\n const client = getSupabaseClient();\n\n logger.info('Opening browser for authentication...');\n\n // Generate magic link\n const { data, error } = await client.auth.signInWithOtp({\n email: '', // Will prompt\n options: {\n shouldCreateUser: false,\n },\n });\n\n if (error) {\n logger.error(`Failed to initiate login: ${error.message}`);\n logger.info('');\n logger.info('Please use email login instead:');\n logger.info(` ${logger.command('playtagon login -e your@email.com')}`);\n process.exit(1);\n }\n\n // For now, fall back to email login since we don't have a callback server\n logger.info('Browser-based login is not yet implemented.');\n logger.info('Please use email login:');\n logger.info(` ${logger.command('playtagon login')}`);\n}\n\n// Hidden password input\nfunction questionHidden(\n prompt: string,\n rl: readline.Interface\n): Promise<string> {\n return new Promise((resolve) => {\n const stdin = process.stdin;\n const stdout = process.stdout;\n\n stdout.write(prompt);\n\n const wasRaw = stdin.isRaw;\n\n if (stdin.isTTY) {\n stdin.setRawMode(true);\n }\n\n let password = '';\n\n const onData = (char: Buffer) => {\n const c = char.toString('utf8');\n\n switch (c) {\n case '\\n':\n case '\\r':\n case '\\u0004':\n // Enter or Ctrl+D\n if (stdin.isTTY) {\n stdin.setRawMode(wasRaw ?? false);\n }\n stdin.removeListener('data', onData);\n stdout.write('\\n');\n resolve(password);\n break;\n case '\\u0003':\n // Ctrl+C\n process.exit(1);\n break;\n case '\\u007F':\n // Backspace\n password = password.slice(0, -1);\n break;\n default:\n password += c;\n break;\n }\n };\n\n stdin.on('data', onData);\n });\n}\n","import { createClient, type SupabaseClient } from '@supabase/supabase-js';\nimport { config, credentials } from './config.js';\nimport { logger } from '../utils/logger.js';\n\nlet supabaseClient: SupabaseClient | null = null;\n\nexport function getSupabaseClient(): SupabaseClient {\n if (!supabaseClient) {\n supabaseClient = createClient(config.supabaseUrl, config.supabaseAnonKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n }\n return supabaseClient;\n}\n\nexport async function getAuthenticatedClient(): Promise<SupabaseClient> {\n const client = getSupabaseClient();\n\n if (!credentials.isLoggedIn()) {\n throw new Error('Not logged in. Run `playtagon login` first.');\n }\n\n // Check if token is expired and needs refresh\n if (credentials.isExpired() && credentials.refreshToken) {\n logger.debug('Token expired, refreshing...');\n await refreshSession();\n }\n\n // Set the session\n const accessToken = credentials.accessToken;\n if (!accessToken) {\n throw new Error('No access token found. Run `playtagon login` first.');\n }\n\n await client.auth.setSession({\n access_token: accessToken,\n refresh_token: credentials.refreshToken || '',\n });\n\n return client;\n}\n\nexport async function refreshSession(): Promise<void> {\n const client = getSupabaseClient();\n const refreshToken = credentials.refreshToken;\n\n if (!refreshToken) {\n throw new Error('No refresh token found. Run `playtagon login` first.');\n }\n\n const { data, error } = await client.auth.refreshSession({\n refresh_token: refreshToken,\n });\n\n if (error) {\n credentials.clear();\n throw new Error(`Failed to refresh session: ${error.message}`);\n }\n\n if (data.session) {\n credentials.save({\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at\n ? data.session.expires_at * 1000\n : Date.now() + 3600 * 1000,\n });\n }\n}\n\nexport async function loginWithEmail(\n email: string,\n password: string\n): Promise<void> {\n const client = getSupabaseClient();\n\n const { data, error } = await client.auth.signInWithPassword({\n email,\n password,\n });\n\n if (error) {\n throw new Error(`Login failed: ${error.message}`);\n }\n\n if (!data.session) {\n throw new Error('Login failed: No session returned');\n }\n\n credentials.save({\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at\n ? data.session.expires_at * 1000\n : Date.now() + 3600 * 1000,\n userId: data.user?.id,\n email: data.user?.email,\n });\n}\n\nexport async function loginWithOAuthUrl(): Promise<string> {\n const client = getSupabaseClient();\n\n const { data, error } = await client.auth.signInWithOAuth({\n provider: 'github',\n options: {\n skipBrowserRedirect: true,\n redirectTo: 'http://localhost:54321/auth/callback',\n },\n });\n\n if (error) {\n throw new Error(`OAuth URL generation failed: ${error.message}`);\n }\n\n return data.url || '';\n}\n\nexport function logout(): void {\n credentials.clear();\n}\n\nexport async function getCurrentUser(): Promise<{\n id: string;\n email: string;\n studios: Array<{ id: string; name: string; slug: string }>;\n} | null> {\n try {\n const client = await getAuthenticatedClient();\n\n const {\n data: { user },\n error,\n } = await client.auth.getUser();\n\n if (error || !user) {\n return null;\n }\n\n // Fetch user's studios\n const { data: memberships } = await client.from('studio_members').select(`\n studio:studios (\n id,\n name,\n slug\n )\n `)\n .eq('user_id', user.id);\n\n const studios =\n memberships\n ?.map((m) => m.studio as unknown as { id: string; name: string; slug: string })\n .filter(Boolean) || [];\n\n return {\n id: user.id,\n email: user.email || '',\n studios,\n };\n } catch {\n return null;\n }\n}\n\nexport function getAccessToken(): string | undefined {\n return credentials.accessToken;\n}\n","import Conf from 'conf';\n\nexport interface PlaytagonConfig {\n supabaseUrl?: string;\n supabaseAnonKey?: string;\n defaultStudio?: string;\n defaultGame?: string;\n}\n\nexport interface PlaytagonCredentials {\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: number;\n userId?: string;\n email?: string;\n}\n\n// Default Supabase config - can be overridden via environment variables\nconst DEFAULT_SUPABASE_URL = process.env.PLAYTAGON_SUPABASE_URL || 'https://yutpzwvdpktvblylglwz.supabase.co';\nconst DEFAULT_SUPABASE_ANON_KEY = process.env.PLAYTAGON_SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1dHB6d3ZkcGt0dmJseWxnbHd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzU4MjMwNjcsImV4cCI6MjA1MTM5OTA2N30.xyNgJxBLPG3W27Lv6oCctRz-DJLy0jxiHCm7W1yQ4j0';\n\n// Config store for settings\nconst configStore = new Conf<PlaytagonConfig>({\n projectName: 'playtagon',\n configFileMode: 0o600,\n defaults: {\n supabaseUrl: DEFAULT_SUPABASE_URL,\n supabaseAnonKey: DEFAULT_SUPABASE_ANON_KEY,\n },\n});\n\n// Separate store for credentials (more sensitive)\nconst credentialsStore = new Conf<PlaytagonCredentials>({\n projectName: 'playtagon',\n configName: 'credentials',\n configFileMode: 0o600,\n});\n\nexport const config = {\n get supabaseUrl(): string {\n return configStore.get('supabaseUrl') || DEFAULT_SUPABASE_URL;\n },\n\n get supabaseAnonKey(): string {\n return configStore.get('supabaseAnonKey') || DEFAULT_SUPABASE_ANON_KEY;\n },\n\n get defaultStudio(): string | undefined {\n return configStore.get('defaultStudio');\n },\n\n set defaultStudio(value: string | undefined) {\n if (value) {\n configStore.set('defaultStudio', value);\n } else {\n configStore.delete('defaultStudio');\n }\n },\n\n get defaultGame(): string | undefined {\n return configStore.get('defaultGame');\n },\n\n set defaultGame(value: string | undefined) {\n if (value) {\n configStore.set('defaultGame', value);\n } else {\n configStore.delete('defaultGame');\n }\n },\n\n // Get full config\n getAll(): PlaytagonConfig {\n return configStore.store;\n },\n\n // Get config file path\n get path(): string {\n return configStore.path;\n },\n};\n\nexport const credentials = {\n get accessToken(): string | undefined {\n return credentialsStore.get('accessToken');\n },\n\n get refreshToken(): string | undefined {\n return credentialsStore.get('refreshToken');\n },\n\n get expiresAt(): number | undefined {\n return credentialsStore.get('expiresAt');\n },\n\n get userId(): string | undefined {\n return credentialsStore.get('userId');\n },\n\n get email(): string | undefined {\n return credentialsStore.get('email');\n },\n\n isLoggedIn(): boolean {\n return !!credentialsStore.get('accessToken');\n },\n\n isExpired(): boolean {\n const expiresAt = credentialsStore.get('expiresAt');\n if (!expiresAt) return true;\n // Consider expired if less than 5 minutes remaining\n return Date.now() > expiresAt - 5 * 60 * 1000;\n },\n\n save(creds: PlaytagonCredentials): void {\n if (creds.accessToken) credentialsStore.set('accessToken', creds.accessToken);\n if (creds.refreshToken) credentialsStore.set('refreshToken', creds.refreshToken);\n if (creds.expiresAt) credentialsStore.set('expiresAt', creds.expiresAt);\n if (creds.userId) credentialsStore.set('userId', creds.userId);\n if (creds.email) credentialsStore.set('email', creds.email);\n },\n\n clear(): void {\n credentialsStore.clear();\n },\n\n // Get credentials file path\n get path(): string {\n return credentialsStore.path;\n },\n};\n","import chalk from 'chalk';\n\nexport const logger = {\n info: (message: string) => {\n console.log(chalk.blue('info'), message);\n },\n\n success: (message: string) => {\n console.log(chalk.green('success'), message);\n },\n\n warn: (message: string) => {\n console.log(chalk.yellow('warn'), message);\n },\n\n error: (message: string) => {\n console.log(chalk.red('error'), message);\n },\n\n debug: (message: string) => {\n if (process.env.DEBUG) {\n console.log(chalk.gray('debug'), message);\n }\n },\n\n // Styled output for specific contexts\n file: (filename: string) => chalk.cyan(filename),\n value: (val: string | number) => chalk.yellow(val),\n command: (cmd: string) => chalk.bold.white(cmd),\n url: (url: string) => chalk.underline.blue(url),\n\n // Section headers\n header: (title: string) => {\n console.log();\n console.log(chalk.bold.white(title));\n console.log(chalk.gray('─'.repeat(title.length)));\n },\n\n // List item\n item: (label: string, value: string) => {\n console.log(` ${chalk.gray(label + ':')} ${value}`);\n },\n\n // Validation result formatting\n validationError: (message: string, detail?: string) => {\n console.log(` ${chalk.red('✗')} ${message}`);\n if (detail) {\n console.log(` ${chalk.gray(detail)}`);\n }\n },\n\n validationSuccess: (message: string) => {\n console.log(` ${chalk.green('✓')} ${message}`);\n },\n\n validationWarn: (message: string) => {\n console.log(` ${chalk.yellow('!')} ${message}`);\n },\n};\n","import { Command } from 'commander';\nimport { logout } from '../lib/auth.js';\nimport { credentials } from '../lib/config.js';\nimport { logger } from '../utils/logger.js';\n\nexport const logoutCommand = new Command('logout')\n .description('Sign out of Playtagon')\n .action(() => {\n if (!credentials.isLoggedIn()) {\n logger.info('Not currently logged in.');\n return;\n }\n\n const email = credentials.email;\n logout();\n\n if (email) {\n logger.success(`Logged out from ${logger.value(email)}`);\n } else {\n logger.success('Logged out successfully');\n }\n });\n","import { Command } from 'commander';\nimport ora from 'ora';\nimport { getCurrentUser } from '../lib/auth.js';\nimport { credentials } from '../lib/config.js';\nimport { logger } from '../utils/logger.js';\n\nexport const whoamiCommand = new Command('whoami')\n .description('Show current logged in user')\n .action(async () => {\n if (!credentials.isLoggedIn()) {\n logger.info('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} to authenticate.`);\n return;\n }\n\n const spinner = ora('Fetching user info...').start();\n\n try {\n const user = await getCurrentUser();\n\n if (!user) {\n spinner.fail('Session expired');\n logger.info(`Run ${logger.command('playtagon login')} to re-authenticate.`);\n return;\n }\n\n spinner.stop();\n\n logger.header('Current User');\n logger.item('ID', user.id);\n logger.item('Email', user.email);\n\n if (user.studios.length > 0) {\n console.log();\n logger.header('Studios');\n for (const studio of user.studios) {\n console.log(` ${studio.name} (${logger.value(studio.slug)})`);\n }\n } else {\n console.log();\n logger.warn('No studios found. Join or create a studio in the platform.');\n }\n } catch (error) {\n spinner.fail('Failed to fetch user info');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport { validateCommand } from './validate.js';\nimport { uploadCommand } from './upload.js';\nimport { presetCommand } from './preset.js';\n\nexport const spineCommand = new Command('spine')\n .description('Manage Spine animation assets')\n .addCommand(validateCommand)\n .addCommand(uploadCommand)\n .addCommand(presetCommand);\n","import { Command } from 'commander';\nimport * as path from 'node:path';\nimport ora from 'ora';\nimport { validateDirectory, type BatchValidation } from '../../lib/spine-validator.js';\nimport { formatFileSize } from '../../utils/files.js';\nimport { logger } from '../../utils/logger.js';\n\nexport const validateCommand = new Command('validate')\n .description('Validate Spine files before uploading')\n .argument('<directory>', 'Directory containing Spine files')\n .option('--batch', 'Enable batch mode for multiple skeletons sharing atlas')\n .action(async (directory: string, options: { batch?: boolean }) => {\n const absolutePath = path.resolve(directory);\n\n const spinner = ora(`Validating ${logger.file(directory)}...`).start();\n\n try {\n const result = validateDirectory(absolutePath, options.batch);\n spinner.stop();\n\n printValidationResult(result, directory);\n\n if (!result.isValid) {\n process.exit(1);\n }\n } catch (error) {\n spinner.fail('Validation failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\nfunction printValidationResult(result: BatchValidation, directory: string): void {\n console.log();\n logger.header(`Validation: ${directory}`);\n\n // Summary info\n if (result.isBatchMode) {\n console.log(` Mode: ${logger.value('Batch')} (${result.assets.length} skeletons)`);\n if (result.sharedAtlas) {\n console.log(` Shared Atlas: ${logger.file(path.basename(result.sharedAtlas))}`);\n console.log(` Shared Textures: ${result.sharedTextures.length}`);\n }\n } else if (result.assets.length > 0) {\n console.log(` Mode: ${logger.value('Single')}`);\n }\n\n console.log(` Total Size: ${logger.value(formatFileSize(result.totalSize))}`);\n console.log();\n\n // Print each asset\n for (const asset of result.assets) {\n const statusIcon = asset.isValid ? '✓' : '✗';\n const statusColor = asset.isValid ? 'green' : 'red';\n\n console.log(\n ` ${statusIcon} ${logger.file(asset.skeleton.name)}`\n );\n console.log(` Spine: ${asset.skeleton.spineVersion}`);\n console.log(` Animations: ${asset.skeleton.animations.length}`);\n console.log(` Skins: ${asset.skeleton.skins.length}`);\n\n if (asset.atlasPath) {\n console.log(` Atlas: ${path.basename(asset.atlasPath)}`);\n console.log(` Textures: ${asset.texturePaths.length}`);\n }\n }\n\n console.log();\n\n // Print all issues\n const errors = result.issues.filter((i) => i.type === 'error');\n const warnings = result.issues.filter((i) => i.type === 'warning');\n const infos = result.issues.filter((i) => i.type === 'info');\n\n // Info messages\n for (const info of infos) {\n logger.info(info.message);\n }\n\n // Warnings\n if (warnings.length > 0) {\n console.log();\n for (const warning of warnings) {\n logger.validationWarn(\n warning.file ? `${warning.file}: ${warning.message}` : warning.message\n );\n if ((warning as { detail?: string }).detail) {\n console.log(` ${(warning as { detail: string }).detail}`);\n }\n }\n }\n\n // Errors\n if (errors.length > 0) {\n console.log();\n for (const error of errors) {\n logger.validationError(\n error.file ? `${error.file}: ${error.message}` : error.message,\n (error as { detail?: string }).detail\n );\n }\n }\n\n // Final status\n console.log();\n if (result.isValid) {\n logger.success('Validation passed! Ready for upload.');\n } else {\n logger.error(`Validation failed with ${errors.length} error(s).`);\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n discoverSpineFiles,\n matchSkeletonToAtlas,\n parseAtlasTextures,\n getFileSize,\n getTotalSize,\n formatFileSize,\n type DiscoveredFiles,\n} from '../utils/files.js';\n\n// Validation rules - keep in sync with server and platform\nexport const SPINE_VALIDATION_RULES = {\n supportedVersions: ['4.0', '4.1', '4.2', '4.3'] as const,\n maxTextureSize: 4096, // pixels per dimension\n maxTotalSize: 50 * 1024 * 1024, // 50MB\n maxTextureCount: 8,\n slugPattern: /^[a-z0-9-]+$/,\n animationNamePattern: /^[a-zA-Z0-9_-]+$/,\n} as const;\n\nexport interface ValidationError {\n type: 'error';\n message: string;\n file?: string;\n detail?: string;\n}\n\nexport interface ValidationWarning {\n type: 'warning';\n message: string;\n file?: string;\n detail?: string;\n}\n\nexport interface ValidationInfo {\n type: 'info';\n message: string;\n}\n\nexport type ValidationIssue = ValidationError | ValidationWarning | ValidationInfo;\n\nexport interface SkeletonInfo {\n path: string;\n name: string;\n spineVersion: string;\n animations: string[];\n skins: string[];\n hasEvents: boolean;\n boneCount: number;\n slotCount: number;\n}\n\nexport interface AssetValidation {\n skeleton: SkeletonInfo;\n atlasPath: string | null;\n texturePaths: string[];\n totalSize: number;\n issues: ValidationIssue[];\n isValid: boolean;\n}\n\nexport interface BatchValidation {\n assets: AssetValidation[];\n sharedAtlas: string | null;\n sharedTextures: string[];\n totalSize: number;\n issues: ValidationIssue[];\n isValid: boolean;\n isBatchMode: boolean;\n}\n\nexport function validateDirectory(\n directory: string,\n batchMode = false\n): BatchValidation {\n const discovered = discoverSpineFiles(directory);\n const issues: ValidationIssue[] = [];\n\n // Check if we found any skeleton files\n if (discovered.skeletons.length === 0) {\n issues.push({\n type: 'error',\n message: 'No Spine skeleton files found',\n detail: 'Expected .json (Spine skeleton) or .skel files',\n });\n return {\n assets: [],\n sharedAtlas: null,\n sharedTextures: [],\n totalSize: 0,\n issues,\n isValid: false,\n isBatchMode: false,\n };\n }\n\n // Determine if this is batch mode (multiple skeletons sharing atlas)\n const isBatchMode = batchMode || discovered.skeletons.length > 1;\n\n if (isBatchMode && discovered.skeletons.length > 1) {\n return validateBatch(discovered, issues);\n } else {\n return validateSingle(discovered, issues);\n }\n}\n\nfunction validateSingle(\n discovered: DiscoveredFiles,\n issues: ValidationIssue[]\n): BatchValidation {\n const skeletonPath = discovered.skeletons[0];\n const skeleton = parseSkeletonFile(skeletonPath);\n\n if (!skeleton) {\n issues.push({\n type: 'error',\n message: 'Failed to parse skeleton file',\n file: path.basename(skeletonPath),\n });\n return {\n assets: [],\n sharedAtlas: null,\n sharedTextures: [],\n totalSize: 0,\n issues,\n isValid: false,\n isBatchMode: false,\n };\n }\n\n // Validate spine version\n const versionIssues = validateSpineVersion(skeleton);\n issues.push(...versionIssues);\n\n // Match atlas\n const atlasPath = matchSkeletonToAtlas(skeletonPath, discovered.atlases);\n if (!atlasPath) {\n if (discovered.atlases.length === 0) {\n issues.push({\n type: 'error',\n message: 'No atlas file found',\n detail: 'Expected .atlas file',\n });\n } else {\n issues.push({\n type: 'warning',\n message: 'Could not match skeleton to atlas',\n detail: `Found atlases: ${discovered.atlases.map((a) => path.basename(a)).join(', ')}`,\n });\n }\n }\n\n // Get textures from atlas\n let texturePaths: string[] = [];\n if (atlasPath) {\n texturePaths = parseAtlasTextures(atlasPath);\n const textureIssues = validateTextures(texturePaths);\n issues.push(...textureIssues);\n }\n\n // Check for orphan textures\n const orphanTextures = discovered.textures.filter(\n (t) => !texturePaths.includes(t)\n );\n if (orphanTextures.length > 0) {\n issues.push({\n type: 'warning',\n message: `${orphanTextures.length} texture(s) not referenced in atlas`,\n detail: orphanTextures.map((t) => path.basename(t)).join(', '),\n });\n }\n\n // Calculate total size\n const allFiles = [skeletonPath, atlasPath, ...texturePaths].filter(\n Boolean\n ) as string[];\n const totalSize = getTotalSize(allFiles);\n\n // Validate total size\n if (totalSize > SPINE_VALIDATION_RULES.maxTotalSize) {\n issues.push({\n type: 'error',\n message: `Total size ${formatFileSize(totalSize)} exceeds ${formatFileSize(SPINE_VALIDATION_RULES.maxTotalSize)} limit`,\n });\n }\n\n // Validate animation names\n const animationIssues = validateAnimationNames(skeleton.animations);\n issues.push(...animationIssues);\n\n const hasErrors = issues.some((i) => i.type === 'error');\n\n const asset: AssetValidation = {\n skeleton,\n atlasPath,\n texturePaths,\n totalSize,\n issues: issues.filter(\n (i) =>\n (i as ValidationError | ValidationWarning).file === undefined ||\n (i as ValidationError | ValidationWarning).file === path.basename(skeletonPath)\n ),\n isValid: !hasErrors,\n };\n\n return {\n assets: [asset],\n sharedAtlas: null,\n sharedTextures: [],\n totalSize,\n issues,\n isValid: !hasErrors,\n isBatchMode: false,\n };\n}\n\nfunction validateBatch(\n discovered: DiscoveredFiles,\n issues: ValidationIssue[]\n): BatchValidation {\n const assets: AssetValidation[] = [];\n\n // In batch mode, we expect a shared atlas\n let sharedAtlas: string | null = null;\n let sharedTextures: string[] = [];\n\n if (discovered.atlases.length === 1) {\n sharedAtlas = discovered.atlases[0];\n sharedTextures = parseAtlasTextures(sharedAtlas);\n\n issues.push({\n type: 'info',\n message: `Batch mode: ${discovered.skeletons.length} skeletons sharing 1 atlas`,\n });\n } else if (discovered.atlases.length === 0) {\n issues.push({\n type: 'error',\n message: 'No atlas file found for batch upload',\n });\n } else {\n issues.push({\n type: 'warning',\n message: `Multiple atlases found (${discovered.atlases.length}). Batch mode works best with shared atlas.`,\n });\n }\n\n // Validate each skeleton\n for (const skeletonPath of discovered.skeletons) {\n const skeleton = parseSkeletonFile(skeletonPath);\n\n if (!skeleton) {\n issues.push({\n type: 'error',\n message: 'Failed to parse skeleton file',\n file: path.basename(skeletonPath),\n });\n continue;\n }\n\n // Validate spine version\n const versionIssues = validateSpineVersion(skeleton);\n issues.push(...versionIssues);\n\n // Validate animation names\n const animationIssues = validateAnimationNames(skeleton.animations);\n issues.push(...animationIssues);\n\n // Each skeleton uses the shared atlas\n const atlasPath = sharedAtlas || matchSkeletonToAtlas(skeletonPath, discovered.atlases);\n const texturePaths = atlasPath ? parseAtlasTextures(atlasPath) : [];\n\n // Calculate size for this asset (skeleton only, atlas counted once at the end)\n const skeletonSize = getFileSize(skeletonPath);\n\n assets.push({\n skeleton,\n atlasPath,\n texturePaths,\n totalSize: skeletonSize,\n issues: [],\n isValid: true,\n });\n }\n\n // Validate shared textures\n if (sharedTextures.length > 0) {\n const textureIssues = validateTextures(sharedTextures);\n issues.push(...textureIssues);\n }\n\n // Calculate total size (atlas + textures counted once)\n const skeletonSizes = assets.reduce((sum, a) => sum + a.totalSize, 0);\n const atlasSize = sharedAtlas ? getFileSize(sharedAtlas) : 0;\n const textureSize = getTotalSize(sharedTextures);\n const totalSize = skeletonSizes + atlasSize + textureSize;\n\n // Validate total size\n if (totalSize > SPINE_VALIDATION_RULES.maxTotalSize) {\n issues.push({\n type: 'error',\n message: `Total batch size ${formatFileSize(totalSize)} exceeds ${formatFileSize(SPINE_VALIDATION_RULES.maxTotalSize)} limit`,\n });\n }\n\n const hasErrors = issues.some((i) => i.type === 'error');\n\n return {\n assets,\n sharedAtlas,\n sharedTextures,\n totalSize,\n issues,\n isValid: !hasErrors,\n isBatchMode: true,\n };\n}\n\nfunction parseSkeletonFile(filePath: string): SkeletonInfo | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const json = JSON.parse(content);\n\n if (!json.skeleton?.spine) {\n return null;\n }\n\n const name = path.basename(filePath, path.extname(filePath));\n const spineVersion = json.skeleton.spine;\n const animations = json.animations ? Object.keys(json.animations) : [];\n const skins = json.skins\n ? Array.isArray(json.skins)\n ? json.skins.map((s: { name?: string }) => s.name || 'default')\n : Object.keys(json.skins)\n : ['default'];\n const hasEvents = !!json.events && Object.keys(json.events).length > 0;\n const boneCount = json.bones ? json.bones.length : 0;\n const slotCount = json.slots ? json.slots.length : 0;\n\n return {\n path: filePath,\n name,\n spineVersion,\n animations,\n skins,\n hasEvents,\n boneCount,\n slotCount,\n };\n } catch {\n return null;\n }\n}\n\nfunction validateSpineVersion(skeleton: SkeletonInfo): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Extract major.minor version\n const version = skeleton.spineVersion;\n const majorMinor = version.match(/^(\\d+\\.\\d+)/)?.[1];\n\n if (\n !majorMinor ||\n !SPINE_VALIDATION_RULES.supportedVersions.includes(\n majorMinor as (typeof SPINE_VALIDATION_RULES.supportedVersions)[number]\n )\n ) {\n issues.push({\n type: 'error',\n message: `Unsupported Spine version: ${version}`,\n file: path.basename(skeleton.path),\n detail: `Supported versions: ${SPINE_VALIDATION_RULES.supportedVersions.join(', ')}`,\n });\n }\n\n return issues;\n}\n\nfunction validateAnimationNames(animations: string[]): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n for (const name of animations) {\n if (!SPINE_VALIDATION_RULES.animationNamePattern.test(name)) {\n issues.push({\n type: 'warning',\n message: `Animation name \"${name}\" contains invalid characters`,\n detail: 'Recommended: a-z, A-Z, 0-9, _, -',\n });\n }\n }\n\n return issues;\n}\n\nfunction validateTextures(texturePaths: string[]): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Check texture count\n if (texturePaths.length > SPINE_VALIDATION_RULES.maxTextureCount) {\n issues.push({\n type: 'error',\n message: `Too many textures: ${texturePaths.length}`,\n detail: `Maximum allowed: ${SPINE_VALIDATION_RULES.maxTextureCount}`,\n });\n }\n\n // Check each texture\n for (const texturePath of texturePaths) {\n if (!fs.existsSync(texturePath)) {\n issues.push({\n type: 'error',\n message: `Texture file not found: ${path.basename(texturePath)}`,\n });\n continue;\n }\n\n // Check file size (basic check - full dimension check would require image parsing)\n const size = getFileSize(texturePath);\n if (size > 20 * 1024 * 1024) {\n // 20MB per texture is suspicious\n issues.push({\n type: 'warning',\n message: `Large texture file: ${path.basename(texturePath)} (${formatFileSize(size)})`,\n detail: 'Consider optimizing texture size',\n });\n }\n }\n\n return issues;\n}\n\nexport function generateSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n}\n\nexport function validateSlug(slug: string): boolean {\n return SPINE_VALIDATION_RULES.slugPattern.test(slug);\n}\n\n// Export preset for Spine Editor\nexport const SPINE_EXPORT_PRESET = {\n class: 'export-json',\n name: 'Playtagon Platform',\n output: '{projectDir}/exports/{skeletonName}',\n extension: '.json',\n format: 'json',\n nonessential: false,\n cleanUp: true,\n warnings: true,\n packAtlas: true,\n packSource: 'attachments',\n packTarget: 'perskeleton',\n packSettings: {\n maxWidth: 4096,\n maxHeight: 4096,\n paddingX: 2,\n paddingY: 2,\n edgePadding: true,\n duplicatePadding: true,\n rotation: true,\n stripWhitespaceX: true,\n stripWhitespaceY: true,\n pot: false,\n filterMin: 'Linear',\n filterMag: 'Linear',\n premultiplyAlpha: true,\n },\n};\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface SpineFileSet {\n skeletonFile: string; // .json or .skel\n atlasFile: string | null; // .atlas\n textureFiles: string[]; // .png or .jpg\n}\n\nexport interface DiscoveredFiles {\n skeletons: string[];\n atlases: string[];\n textures: string[];\n}\n\n// File extensions\nconst SKELETON_EXTENSIONS = ['.json', '.skel'];\nconst ATLAS_EXTENSIONS = ['.atlas'];\nconst TEXTURE_EXTENSIONS = ['.png', '.jpg', '.jpeg'];\n\nexport function discoverSpineFiles(directory: string): DiscoveredFiles {\n const absolutePath = path.resolve(directory);\n\n if (!fs.existsSync(absolutePath)) {\n throw new Error(`Directory not found: ${directory}`);\n }\n\n if (!fs.statSync(absolutePath).isDirectory()) {\n throw new Error(`Not a directory: ${directory}`);\n }\n\n const files = fs.readdirSync(absolutePath);\n\n const skeletons: string[] = [];\n const atlases: string[] = [];\n const textures: string[] = [];\n\n for (const file of files) {\n const ext = path.extname(file).toLowerCase();\n const fullPath = path.join(absolutePath, file);\n\n // Skip directories\n if (fs.statSync(fullPath).isDirectory()) continue;\n\n if (SKELETON_EXTENSIONS.includes(ext)) {\n // For .json files, we need to verify it's actually a Spine skeleton\n if (ext === '.json') {\n if (isSpineSkeletonJson(fullPath)) {\n skeletons.push(fullPath);\n }\n } else {\n skeletons.push(fullPath);\n }\n } else if (ATLAS_EXTENSIONS.includes(ext)) {\n atlases.push(fullPath);\n } else if (TEXTURE_EXTENSIONS.includes(ext)) {\n textures.push(fullPath);\n }\n }\n\n return { skeletons, atlases, textures };\n}\n\nexport function isSpineSkeletonJson(filePath: string): boolean {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const json = JSON.parse(content);\n // Spine skeleton files have a \"skeleton\" property with version info\n return json.skeleton && typeof json.skeleton.spine === 'string';\n } catch {\n return false;\n }\n}\n\nexport function matchSkeletonToAtlas(\n skeletonPath: string,\n atlases: string[]\n): string | null {\n const skeletonName = path.basename(skeletonPath, path.extname(skeletonPath));\n const skeletonDir = path.dirname(skeletonPath);\n\n // Try exact name match first\n for (const atlas of atlases) {\n const atlasName = path.basename(atlas, '.atlas');\n if (atlasName === skeletonName) {\n return atlas;\n }\n }\n\n // If only one atlas in same directory, use it\n const sameDirectoryAtlases = atlases.filter(\n (a) => path.dirname(a) === skeletonDir\n );\n if (sameDirectoryAtlases.length === 1) {\n return sameDirectoryAtlases[0];\n }\n\n return null;\n}\n\nexport function parseAtlasTextures(atlasPath: string): string[] {\n const content = fs.readFileSync(atlasPath, 'utf-8');\n const lines = content.split('\\n');\n const textures: string[] = [];\n const atlasDir = path.dirname(atlasPath);\n\n for (const line of lines) {\n const trimmed = line.trim();\n // Texture file names are on their own lines, usually end with .png\n if (trimmed.endsWith('.png') || trimmed.endsWith('.jpg')) {\n const texturePath = path.join(atlasDir, trimmed);\n if (fs.existsSync(texturePath)) {\n textures.push(texturePath);\n }\n }\n }\n\n return textures;\n}\n\nexport function getFileSize(filePath: string): number {\n return fs.statSync(filePath).size;\n}\n\nexport function getTotalSize(files: string[]): number {\n return files.reduce((total, file) => total + getFileSize(file), 0);\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\nexport function readFileAsBuffer(filePath: string): Buffer {\n return fs.readFileSync(filePath);\n}\n\nexport function getBaseName(filePath: string): string {\n return path.basename(filePath);\n}\n\nexport function getExtension(filePath: string): string {\n return path.extname(filePath).toLowerCase();\n}\n","import { Command } from 'commander';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport ora from 'ora';\nimport { credentials, config } from '../../lib/config.js';\nimport { getAccessToken } from '../../lib/auth.js';\nimport { uploadSpineAsset, resolveStudio, resolveGame } from '../../lib/api.js';\nimport {\n validateDirectory,\n generateSlug,\n validateSlug,\n type BatchValidation,\n} from '../../lib/spine-validator.js';\nimport { formatFileSize, getBaseName } from '../../utils/files.js';\nimport { logger } from '../../utils/logger.js';\n\nexport const uploadCommand = new Command('upload')\n .description('Upload Spine files to Playtagon')\n .argument('<directory>', 'Directory containing Spine files')\n .option('-s, --studio <studio>', 'Studio ID or slug (uses default if set)')\n .option('-g, --game <game>', 'Game ID or slug (uses default if set)')\n .option('-n, --name <name>', 'Asset name (defaults to directory name)')\n .option('--slug <slug>', 'Custom slug for the asset')\n .option('--batch', 'Enable batch mode for multiple skeletons sharing atlas')\n .option('--dry-run', 'Validate only, do not upload')\n .option('--description <text>', 'Asset description')\n .option('--tags <tags>', 'Comma-separated tags')\n .action(async (directory: string, options) => {\n // Check authentication\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n // Use defaults from config if not provided\n const studioOption = options.studio || config.defaultStudio;\n const gameOption = options.game || config.defaultGame;\n\n if (!studioOption) {\n logger.error('Studio is required.');\n logger.info(`Either provide ${logger.command('--studio <slug>')} or set a default:`);\n logger.info(` ${logger.command('playtagon config --studio <slug>')}`);\n logger.info(` ${logger.command('playtagon setup spine-integration --studio <slug>')}`);\n process.exit(1);\n }\n\n const absolutePath = path.resolve(directory);\n\n // Validate directory first\n const spinner = ora('Validating files...').start();\n\n let validation: BatchValidation;\n try {\n validation = validateDirectory(absolutePath, options.batch);\n } catch (error) {\n spinner.fail('Validation failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n\n if (!validation.isValid) {\n spinner.fail('Validation failed');\n printValidationErrors(validation);\n process.exit(1);\n }\n\n spinner.succeed(`Validated ${validation.assets.length} asset(s)`);\n\n // Resolve studio\n spinner.start('Resolving studio...');\n const studio = await resolveStudio(studioOption);\n if (!studio) {\n spinner.fail('Studio not found');\n logger.error(`Studio \"${studioOption}\" not found or you don't have access.`);\n process.exit(1);\n }\n spinner.succeed(`Studio: ${studio.name}`);\n\n // Resolve game if provided\n let game = null;\n if (gameOption) {\n spinner.start('Resolving game...');\n game = await resolveGame(studio.id, gameOption);\n if (!game) {\n spinner.fail('Game not found');\n logger.error(`Game \"${gameOption}\" not found in studio \"${studio.name}\".`);\n process.exit(1);\n }\n spinner.succeed(`Game: ${game.name}`);\n }\n\n // Determine asset name\n const assetName = options.name || path.basename(absolutePath);\n\n // Generate or validate slug\n let slug = options.slug || generateSlug(assetName);\n if (!validateSlug(slug)) {\n logger.error(`Invalid slug: \"${slug}\". Use lowercase letters, numbers, and hyphens only.`);\n process.exit(1);\n }\n\n // Print upload summary\n console.log();\n logger.header('Upload Summary');\n logger.item('Directory', directory);\n logger.item('Studio', studio.name);\n if (game) logger.item('Game', game.name);\n logger.item('Name', assetName);\n logger.item('Slug', slug);\n logger.item('Size', formatFileSize(validation.totalSize));\n\n if (validation.isBatchMode) {\n logger.item('Mode', 'Batch');\n logger.item('Skeletons', String(validation.assets.length));\n }\n\n if (options.dryRun) {\n console.log();\n logger.success('Dry run complete. No files were uploaded.');\n return;\n }\n\n // Build FormData for upload\n console.log();\n spinner.start('Uploading...');\n\n try {\n const formData = await buildFormData(validation, {\n studioId: studio.id,\n gameId: game?.id,\n name: assetName,\n slug,\n description: options.description,\n tags: options.tags?.split(',').map((t: string) => t.trim()),\n batchMode: validation.isBatchMode,\n });\n\n const result = await uploadSpineAsset(formData);\n\n if (result.success) {\n spinner.succeed('Upload complete!');\n\n console.log();\n if (result.assets && result.assets.length > 1) {\n logger.header('Uploaded Assets');\n for (const asset of result.assets) {\n console.log(` ${logger.value(asset.name)} (${asset.slug})`);\n console.log(` ID: ${asset.id}`);\n console.log(` Status: ${asset.status}`);\n }\n } else if (result.asset) {\n logger.header('Uploaded Asset');\n logger.item('ID', result.asset.id);\n logger.item('Name', result.asset.name);\n logger.item('Slug', result.asset.slug);\n logger.item('Status', result.asset.status);\n }\n } else {\n spinner.fail('Upload failed');\n logger.error(result.error || 'Unknown error');\n process.exit(1);\n }\n } catch (error) {\n spinner.fail('Upload failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\nasync function buildFormData(\n validation: BatchValidation,\n options: {\n studioId: string;\n gameId?: string;\n name: string;\n slug: string;\n description?: string;\n tags?: string[];\n batchMode: boolean;\n }\n): Promise<FormData> {\n const formData = new FormData();\n\n // Add metadata\n formData.append('studioId', options.studioId);\n if (options.gameId) formData.append('gameId', options.gameId);\n formData.append('name', options.name);\n formData.append('slug', options.slug);\n if (options.description) formData.append('description', options.description);\n if (options.tags) formData.append('tags', JSON.stringify(options.tags));\n if (options.batchMode) formData.append('batchMode', 'true');\n\n // Collect all unique files\n const filesAdded = new Set<string>();\n\n // Add skeleton files\n for (const asset of validation.assets) {\n const filePath = asset.skeleton.path;\n if (!filesAdded.has(filePath)) {\n const buffer = fs.readFileSync(filePath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(filePath));\n filesAdded.add(filePath);\n }\n }\n\n // Add shared atlas if exists\n if (validation.sharedAtlas && !filesAdded.has(validation.sharedAtlas)) {\n const buffer = fs.readFileSync(validation.sharedAtlas);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(validation.sharedAtlas));\n filesAdded.add(validation.sharedAtlas);\n }\n\n // Add shared textures\n for (const texturePath of validation.sharedTextures) {\n if (!filesAdded.has(texturePath)) {\n const buffer = fs.readFileSync(texturePath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(texturePath));\n filesAdded.add(texturePath);\n }\n }\n\n // Add individual asset files (atlas and textures for non-batch mode)\n for (const asset of validation.assets) {\n if (asset.atlasPath && !filesAdded.has(asset.atlasPath)) {\n const buffer = fs.readFileSync(asset.atlasPath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(asset.atlasPath));\n filesAdded.add(asset.atlasPath);\n }\n\n for (const texturePath of asset.texturePaths) {\n if (!filesAdded.has(texturePath)) {\n const buffer = fs.readFileSync(texturePath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(texturePath));\n filesAdded.add(texturePath);\n }\n }\n }\n\n return formData;\n}\n\nfunction printValidationErrors(validation: BatchValidation): void {\n const errors = validation.issues.filter((i) => i.type === 'error');\n console.log();\n for (const error of errors) {\n logger.validationError(\n error.file ? `${error.file}: ${error.message}` : error.message,\n (error as { detail?: string }).detail\n );\n }\n console.log();\n logger.info(`Run ${logger.command('playtagon spine validate <dir>')} for full report.`);\n}\n","import { config } from './config.js';\nimport { getAccessToken } from './auth.js';\n\nconst API_BASE = config.supabaseUrl;\n\nexport interface UploadResponse {\n success: boolean;\n asset?: {\n id: string;\n name: string;\n slug: string;\n version: number;\n status: string;\n };\n assets?: Array<{\n id: string;\n name: string;\n slug: string;\n version: number;\n status: string;\n }>;\n error?: string;\n}\n\nexport interface StudioInfo {\n id: string;\n name: string;\n slug: string;\n}\n\nexport interface GameInfo {\n id: string;\n name: string;\n slug: string;\n}\n\nexport async function uploadSpineAsset(formData: FormData): Promise<UploadResponse> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const response = await fetch(`${API_BASE}/functions/v1/spine-upload`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error || errorJson.message || errorText;\n } catch {\n errorMessage = errorText;\n }\n throw new Error(`Upload failed: ${errorMessage}`);\n }\n\n return response.json();\n}\n\nexport async function getStudios(): Promise<StudioInfo[]> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const response = await fetch(\n `${API_BASE}/rest/v1/studio_members?select=studio:studios(id,name,slug)`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch studios');\n }\n\n const data = await response.json();\n return data.map((m: { studio: StudioInfo }) => m.studio).filter(Boolean);\n}\n\nexport async function getGames(studioId: string): Promise<GameInfo[]> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const response = await fetch(\n `${API_BASE}/rest/v1/games?studio_id=eq.${studioId}&select=id,name,slug`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch games');\n }\n\n return response.json();\n}\n\nexport async function resolveStudio(studioIdOrSlug: string): Promise<StudioInfo | null> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n // Try by ID first, then by slug\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(\n studioIdOrSlug\n );\n\n const query = isUuid\n ? `id=eq.${studioIdOrSlug}`\n : `slug=eq.${studioIdOrSlug}`;\n\n const response = await fetch(\n `${API_BASE}/rest/v1/studios?${query}&select=id,name,slug`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n return data[0] || null;\n}\n\nexport async function resolveGame(\n studioId: string,\n gameIdOrSlug: string\n): Promise<GameInfo | null> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(\n gameIdOrSlug\n );\n\n const idQuery = isUuid\n ? `id=eq.${gameIdOrSlug}`\n : `slug=eq.${gameIdOrSlug}`;\n\n const response = await fetch(\n `${API_BASE}/rest/v1/games?studio_id=eq.${studioId}&${idQuery}&select=id,name,slug`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n return data[0] || null;\n}\n","import { Command } from 'commander';\nimport { SPINE_EXPORT_PRESET } from '../../lib/spine-validator.js';\nimport { logger } from '../../utils/logger.js';\n\nexport const presetCommand = new Command('preset')\n .description('Output Spine export preset JSON for consistent exports')\n .option('--pretty', 'Pretty print JSON (default: true)', true)\n .option('--compact', 'Output compact JSON')\n .action((options) => {\n const pretty = !options.compact;\n\n if (pretty) {\n // Print usage info to stderr so it doesn't interfere with piping\n console.error(logger.info('Spine Export Preset for Playtagon Platform'));\n console.error();\n console.error('Usage:');\n console.error(' playtagon spine preset > playtagon-export.json');\n console.error(' Import this file into Spine Editor as an export preset.');\n console.error();\n }\n\n // Output JSON to stdout for easy piping\n const json = pretty\n ? JSON.stringify(SPINE_EXPORT_PRESET, null, 2)\n : JSON.stringify(SPINE_EXPORT_PRESET);\n\n console.log(json);\n });\n","import { Command } from 'commander';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport ora from 'ora';\nimport { credentials, config } from '../lib/config.js';\nimport { getCurrentUser } from '../lib/auth.js';\nimport { getStudios, getGames } from '../lib/api.js';\nimport { SPINE_EXPORT_PRESET } from '../lib/spine-validator.js';\nimport { logger } from '../utils/logger.js';\n\nconst PLAYTAGON_DIR = path.join(os.homedir(), '.playtagon');\n\nexport const setupCommand = new Command('setup')\n .description('Set up integrations')\n .addCommand(spineIntegrationCommand());\n\nfunction spineIntegrationCommand(): Command {\n return new Command('spine-integration')\n .description('Set up automatic Spine Editor to Platform upload')\n .option('-s, --studio <studio>', 'Default studio for uploads')\n .option('-g, --game <game>', 'Default game for uploads')\n .option('--force', 'Overwrite existing setup files')\n .action(async (options) => {\n // Check authentication\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n const spinner = ora('Checking authentication...').start();\n\n // Verify auth and get user info\n const user = await getCurrentUser();\n if (!user) {\n spinner.fail('Session expired');\n logger.info(`Run ${logger.command('playtagon login')} to re-authenticate.`);\n process.exit(1);\n }\n\n spinner.succeed(`Logged in as ${user.email}`);\n\n // Get studios\n let studioSlug = options.studio;\n if (!studioSlug) {\n spinner.start('Fetching studios...');\n const studios = await getStudios();\n spinner.stop();\n\n if (studios.length === 0) {\n logger.error('No studios found. Join or create a studio first.');\n process.exit(1);\n }\n\n if (studios.length === 1) {\n studioSlug = studios[0].slug;\n logger.info(`Using studio: ${logger.value(studios[0].name)}`);\n } else {\n logger.header('Available Studios');\n studios.forEach((s, i) => {\n console.log(` ${i + 1}. ${s.name} (${logger.value(s.slug)})`);\n });\n console.log();\n logger.info(`Specify studio with ${logger.command('--studio <slug>')}`);\n logger.info(`Example: ${logger.command(`playtagon setup spine-integration --studio ${studios[0].slug}`)}`);\n process.exit(1);\n }\n }\n\n // Create .playtagon directory\n spinner.start('Creating setup files...');\n\n if (!fs.existsSync(PLAYTAGON_DIR)) {\n fs.mkdirSync(PLAYTAGON_DIR, { recursive: true, mode: 0o700 });\n }\n\n // Generate post-export scripts\n const scriptPaths = generatePostExportScripts(studioSlug, options.game, options.force);\n\n // Generate Spine export preset with postScript\n const presetPath = generateExportPreset(scriptPaths.sh, options.force);\n\n spinner.succeed('Setup complete!');\n\n // Print instructions\n console.log();\n logger.header('Setup Complete');\n console.log();\n logger.info('Files created:');\n console.log(` ${logger.file(scriptPaths.sh)}`);\n if (process.platform === 'win32') {\n console.log(` ${logger.file(scriptPaths.bat)}`);\n }\n console.log(` ${logger.file(presetPath)}`);\n\n console.log();\n logger.header('Next Steps');\n console.log();\n console.log(' 1. Open Spine Editor');\n console.log(' 2. Go to File → Export...');\n console.log(' 3. Click the gear icon (⚙) next to the preset dropdown');\n console.log(' 4. Click \"Import\" and select:');\n console.log(` ${logger.file(presetPath)}`);\n console.log(' 5. The preset \"Upload to Playtagon\" will be available');\n console.log();\n console.log(' Now when you export with this preset, files automatically');\n console.log(' upload to Playtagon Platform!');\n console.log();\n\n // Verify game if provided\n let gameSlug = options.game;\n if (gameSlug) {\n spinner.start('Verifying game...');\n const studios = await getStudios();\n const studio = studios.find((s) => s.slug === studioSlug);\n if (studio) {\n const games = await getGames(studio.id);\n const game = games.find(\n (g) => g.slug === gameSlug || g.id === gameSlug || g.name === gameSlug\n );\n if (!game) {\n spinner.fail('Game not found');\n logger.error(`Game \"${gameSlug}\" not found in studio \"${studioSlug}\".`);\n if (games.length > 0) {\n console.log();\n logger.info('Available games:');\n games.forEach((g) => {\n console.log(` ${logger.value(g.slug)} - ${g.name}`);\n });\n }\n process.exit(1);\n }\n gameSlug = game.slug; // Normalize to slug\n spinner.succeed(`Game: ${game.name}`);\n }\n }\n\n // Save defaults to config\n config.defaultStudio = studioSlug;\n if (gameSlug) {\n config.defaultGame = gameSlug;\n }\n\n if (gameSlug) {\n logger.info(`Uploads will go to: ${logger.value(studioSlug)} / ${logger.value(gameSlug)}`);\n } else {\n logger.info(`Uploads will go to studio: ${logger.value(studioSlug)}`);\n logger.info(`Add ${logger.command('--game <slug>')} to also set default game.`);\n }\n\n console.log();\n logger.info('Defaults saved. You can change them later with:');\n logger.info(` ${logger.command('playtagon config --studio <slug> --game <slug>')}`);\n });\n}\n\nfunction generatePostExportScripts(\n studioSlug: string,\n gameSlug?: string,\n force = false\n): { sh: string; bat: string } {\n const shPath = path.join(PLAYTAGON_DIR, 'upload.sh');\n const batPath = path.join(PLAYTAGON_DIR, 'upload.bat');\n\n // Build upload command\n let uploadCmd = `playtagon spine upload \"$1\" --studio ${studioSlug}`;\n if (gameSlug) {\n uploadCmd += ` --game ${gameSlug}`;\n }\n\n // Shell script (Mac/Linux)\n const shScript = `#!/bin/bash\n# Playtagon Spine Upload Script\n# Auto-generated by: playtagon setup spine-integration\n#\n# This script is called by Spine Editor after export.\n# It uploads the exported files to Playtagon Platform.\n\nset -e\n\nEXPORT_DIR=\"$1\"\n\nif [ -z \"$EXPORT_DIR\" ]; then\n echo \"Error: No export directory provided\"\n exit 1\nfi\n\necho \"Uploading to Playtagon...\"\n${uploadCmd}\n\n# macOS notification (optional)\nif command -v osascript &> /dev/null; then\n osascript -e 'display notification \"Spine export uploaded successfully\" with title \"Playtagon\"' 2>/dev/null || true\nfi\n\n# Linux notification (optional)\nif command -v notify-send &> /dev/null; then\n notify-send \"Playtagon\" \"Spine export uploaded successfully\" 2>/dev/null || true\nfi\n\necho \"Upload complete!\"\n`;\n\n // Batch script (Windows)\n let uploadCmdWin = `playtagon spine upload \"%~1\" --studio ${studioSlug}`;\n if (gameSlug) {\n uploadCmdWin += ` --game ${gameSlug}`;\n }\n\n const batScript = `@echo off\nREM Playtagon Spine Upload Script\nREM Auto-generated by: playtagon setup spine-integration\nREM\nREM This script is called by Spine Editor after export.\nREM It uploads the exported files to Playtagon Platform.\n\nset EXPORT_DIR=%~1\n\nif \"%EXPORT_DIR%\"==\"\" (\n echo Error: No export directory provided\n exit /b 1\n)\n\necho Uploading to Playtagon...\n${uploadCmdWin}\n\nREM Windows notification (PowerShell)\npowershell -Command \"& {Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('Spine export uploaded successfully', 'Playtagon', 'OK', 'Information')}\" 2>nul\n\necho Upload complete!\n`;\n\n // Write scripts\n if (!fs.existsSync(shPath) || force) {\n fs.writeFileSync(shPath, shScript, { mode: 0o755 });\n } else {\n logger.warn(`${shPath} already exists. Use --force to overwrite.`);\n }\n\n if (!fs.existsSync(batPath) || force) {\n fs.writeFileSync(batPath, batScript);\n }\n\n return { sh: shPath, bat: batPath };\n}\n\nfunction generateExportPreset(scriptPath: string, force = false): string {\n const presetPath = path.join(PLAYTAGON_DIR, 'playtagon-spine-preset.export.json');\n\n // Extend the base preset with postScript\n const preset = {\n ...SPINE_EXPORT_PRESET,\n name: 'Upload to Playtagon',\n postScript: process.platform === 'win32'\n ? `\"${scriptPath.replace('.sh', '.bat')}\" \"{output}\"`\n : `\"${scriptPath}\" \"{output}\"`,\n };\n\n if (!fs.existsSync(presetPath) || force) {\n fs.writeFileSync(presetPath, JSON.stringify(preset, null, 2));\n } else {\n logger.warn(`${presetPath} already exists. Use --force to overwrite.`);\n }\n\n return presetPath;\n}\n","import { Command } from 'commander';\nimport { config, credentials } from '../lib/config.js';\nimport { getStudios, getGames } from '../lib/api.js';\nimport { logger } from '../utils/logger.js';\nimport ora from 'ora';\n\nexport const configCommand = new Command('config')\n .description('View or set CLI configuration')\n .option('-s, --studio <studio>', 'Set default studio')\n .option('-g, --game <game>', 'Set default game')\n .option('--clear', 'Clear all default settings')\n .action(async (options) => {\n // If --clear flag is set, clear all defaults\n if (options.clear) {\n config.defaultStudio = undefined;\n config.defaultGame = undefined;\n logger.success('Default settings cleared.');\n return;\n }\n\n // If setting studio\n if (options.studio) {\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n const spinner = ora('Verifying studio...').start();\n const studios = await getStudios();\n const studio = studios.find(\n (s) => s.slug === options.studio || s.id === options.studio || s.name === options.studio\n );\n\n if (!studio) {\n spinner.fail('Studio not found');\n logger.error(`Studio \"${options.studio}\" not found or you don't have access.`);\n console.log();\n logger.info('Available studios:');\n studios.forEach((s) => {\n console.log(` ${logger.value(s.slug)} - ${s.name}`);\n });\n process.exit(1);\n }\n\n spinner.succeed(`Default studio set: ${studio.name} (${studio.slug})`);\n config.defaultStudio = studio.slug;\n }\n\n // If setting game\n if (options.game) {\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n const studioSlug = options.studio || config.defaultStudio;\n if (!studioSlug) {\n logger.error('Studio is required to set default game.');\n logger.info(`Either provide ${logger.command('--studio <slug>')} or set a default studio first.`);\n process.exit(1);\n }\n\n const spinner = ora('Verifying game...').start();\n const studios = await getStudios();\n const studio = studios.find(\n (s) => s.slug === studioSlug || s.id === studioSlug || s.name === studioSlug\n );\n\n if (!studio) {\n spinner.fail('Studio not found');\n process.exit(1);\n }\n\n const games = await getGames(studio.id);\n const game = games.find(\n (g) => g.slug === options.game || g.id === options.game || g.name === options.game\n );\n\n if (!game) {\n spinner.fail('Game not found');\n logger.error(`Game \"${options.game}\" not found in studio \"${studio.name}\".`);\n console.log();\n if (games.length > 0) {\n logger.info('Available games:');\n games.forEach((g) => {\n console.log(` ${logger.value(g.slug)} - ${g.name}`);\n });\n } else {\n logger.info('No games in this studio yet.');\n }\n process.exit(1);\n }\n\n spinner.succeed(`Default game set: ${game.name} (${game.slug})`);\n config.defaultGame = game.slug;\n }\n\n // If no options, show current config\n if (!options.studio && !options.game && !options.clear) {\n logger.header('Current Configuration');\n console.log();\n logger.item('Config file', config.path);\n console.log();\n\n const currentStudio = config.defaultStudio;\n const currentGame = config.defaultGame;\n\n if (currentStudio || currentGame) {\n logger.header('Default Upload Target');\n if (currentStudio) {\n logger.item('Studio', currentStudio);\n }\n if (currentGame) {\n logger.item('Game', currentGame);\n }\n console.log();\n logger.info('These defaults are used when --studio/--game are not provided.');\n console.log();\n logger.info(`To change: ${logger.command('playtagon config --studio <slug> --game <slug>')}`);\n logger.info(`To clear: ${logger.command('playtagon config --clear')}`);\n } else {\n logger.info('No default studio or game set.');\n console.log();\n logger.info(`Set defaults: ${logger.command('playtagon config --studio <slug> --game <slug>')}`);\n logger.info(`Or run setup: ${logger.command('playtagon setup spine-integration')}`);\n }\n }\n });\n"],"mappings":"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","names":["Command","resolve","Command","Command","Command","ora","Command","ora","Command","Command","path","ora","fs","path","Command","ora","Command","fs","path","ora","Command","ora","Command","Command","Command","Command","fs","path","ora","Command","ora","Command","ora","Command","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/login.ts","../src/lib/auth.ts","../src/lib/config.ts","../src/utils/logger.ts","../src/commands/logout.ts","../src/commands/whoami.ts","../src/commands/spine/index.ts","../src/commands/spine/validate.ts","../src/lib/spine-validator.ts","../src/utils/files.ts","../src/commands/spine/upload.ts","../src/lib/api.ts","../src/commands/spine/preset.ts","../src/commands/setup.ts","../src/commands/config.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { loginCommand } from './commands/login.js';\nimport { logoutCommand } from './commands/logout.js';\nimport { whoamiCommand } from './commands/whoami.js';\nimport { spineCommand } from './commands/spine/index.js';\nimport { setupCommand } from './commands/setup.js';\nimport { configCommand } from './commands/config.js';\n\nconst program = new Command();\n\nprogram\n .name('playtagon')\n .description('Playtagon CLI - Upload and manage game assets')\n .version('0.2.0');\n\n// Add commands\nprogram.addCommand(loginCommand);\nprogram.addCommand(logoutCommand);\nprogram.addCommand(whoamiCommand);\nprogram.addCommand(spineCommand);\nprogram.addCommand(setupCommand);\nprogram.addCommand(configCommand);\n\n// Parse arguments\nprogram.parse();\n","import { Command } from 'commander';\nimport * as readline from 'node:readline';\nimport open from 'open';\nimport ora from 'ora';\nimport { loginWithEmail, loginWithBrowser, getAuthUrl, getCurrentUser } from '../lib/auth.js';\nimport { credentials } from '../lib/config.js';\nimport { logger } from '../utils/logger.js';\n\nexport const loginCommand = new Command('login')\n .description('Authenticate with Playtagon')\n .option('-e, --email <email>', 'Login with email/password instead of browser')\n .option('--no-browser', 'Use email/password login instead of browser')\n .action(async (options) => {\n // Check if already logged in\n if (credentials.isLoggedIn()) {\n const user = await getCurrentUser();\n if (user) {\n logger.info(`Already logged in as ${logger.value(user.email)}`);\n logger.info(`Run ${logger.command('playtagon logout')} to sign out.`);\n return;\n }\n }\n\n if (options.email) {\n await emailLogin(options.email);\n } else if (options.browser === false) {\n await promptEmailLogin();\n } else {\n // Default: browser-based login (like npm, gh, vercel)\n await browserLogin();\n }\n });\n\nasync function promptEmailLogin(): Promise<void> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const question = (prompt: string): Promise<string> =>\n new Promise((resolve) => {\n rl.question(prompt, resolve);\n });\n\n try {\n const email = await question('Email: ');\n const password = await questionHidden('Password: ', rl);\n\n const spinner = ora('Logging in...').start();\n\n try {\n await loginWithEmail(email, password);\n spinner.succeed('Logged in successfully!');\n\n const user = await getCurrentUser();\n if (user) {\n logger.header('Account');\n logger.item('Email', user.email);\n if (user.studios.length > 0) {\n logger.item(\n 'Studios',\n user.studios.map((s) => s.name).join(', ')\n );\n }\n }\n } catch (error) {\n spinner.fail('Login failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n } finally {\n rl.close();\n }\n}\n\nasync function emailLogin(email: string): Promise<void> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n try {\n const password = await questionHidden('Password: ', rl);\n\n const spinner = ora('Logging in...').start();\n\n try {\n await loginWithEmail(email, password);\n spinner.succeed('Logged in successfully!');\n\n const user = await getCurrentUser();\n if (user) {\n logger.header('Account');\n logger.item('Email', user.email);\n }\n } catch (error) {\n spinner.fail('Login failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n } finally {\n rl.close();\n }\n}\n\nasync function browserLogin(): Promise<void> {\n const authUrl = getAuthUrl();\n\n console.log();\n logger.info('Opening browser to log in...');\n logger.info('');\n logger.info(`If browser doesn't open, visit:`);\n logger.info(` ${logger.link(authUrl)}`);\n console.log();\n\n // Open browser\n try {\n await open(authUrl);\n } catch {\n // Browser failed to open, user can use the URL manually\n }\n\n const spinner = ora('Waiting for authentication...').start();\n\n // Start local server and wait for callback\n const result = await loginWithBrowser();\n\n if (result.success) {\n spinner.succeed('Logged in successfully!');\n\n const user = await getCurrentUser();\n if (user) {\n console.log();\n logger.header('Account');\n logger.item('Email', user.email);\n if (user.studios.length > 0) {\n logger.item(\n 'Studios',\n user.studios.map((s) => s.name).join(', ')\n );\n }\n }\n } else {\n spinner.fail('Login failed');\n logger.error(result.error || 'Unknown error');\n console.log();\n logger.info('You can also try email/password login:');\n logger.info(` ${logger.command('playtagon login --no-browser')}`);\n process.exit(1);\n }\n}\n\n// Hidden password input\nfunction questionHidden(\n prompt: string,\n rl: readline.Interface\n): Promise<string> {\n return new Promise((resolve) => {\n const stdin = process.stdin;\n const stdout = process.stdout;\n\n stdout.write(prompt);\n\n const wasRaw = stdin.isRaw;\n\n if (stdin.isTTY) {\n stdin.setRawMode(true);\n }\n\n let password = '';\n\n const onData = (char: Buffer) => {\n const c = char.toString('utf8');\n\n switch (c) {\n case '\\n':\n case '\\r':\n case '\\u0004':\n // Enter or Ctrl+D\n if (stdin.isTTY) {\n stdin.setRawMode(wasRaw ?? false);\n }\n stdin.removeListener('data', onData);\n stdout.write('\\n');\n resolve(password);\n break;\n case '\\u0003':\n // Ctrl+C\n process.exit(1);\n break;\n case '\\u007F':\n // Backspace\n password = password.slice(0, -1);\n break;\n default:\n password += c;\n break;\n }\n };\n\n stdin.on('data', onData);\n });\n}\n","import { createClient, type SupabaseClient } from '@supabase/supabase-js';\nimport * as http from 'node:http';\nimport * as crypto from 'node:crypto';\nimport { config, credentials } from './config.js';\nimport { logger } from '../utils/logger.js';\n\nlet supabaseClient: SupabaseClient | null = null;\n\n// PKCE helpers\nfunction base64URLEncode(buffer: Buffer): string {\n return buffer.toString('base64').replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\nfunction sha256(buffer: string): Buffer {\n return crypto.createHash('sha256').update(buffer).digest();\n}\n\nfunction generateCodeVerifier(): string {\n return base64URLEncode(crypto.randomBytes(32));\n}\n\nfunction generateCodeChallenge(verifier: string): string {\n return base64URLEncode(sha256(verifier));\n}\n\nexport function getSupabaseClient(): SupabaseClient {\n if (!supabaseClient) {\n supabaseClient = createClient(config.supabaseUrl, config.supabaseAnonKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n }\n return supabaseClient;\n}\n\nexport async function getAuthenticatedClient(): Promise<SupabaseClient> {\n const client = getSupabaseClient();\n\n if (!credentials.isLoggedIn()) {\n throw new Error('Not logged in. Run `playtagon login` first.');\n }\n\n // Check if token is expired and needs refresh\n if (credentials.isExpired() && credentials.refreshToken) {\n logger.debug('Token expired, refreshing...');\n await refreshSession();\n }\n\n // Set the session\n const accessToken = credentials.accessToken;\n if (!accessToken) {\n throw new Error('No access token found. Run `playtagon login` first.');\n }\n\n await client.auth.setSession({\n access_token: accessToken,\n refresh_token: credentials.refreshToken || '',\n });\n\n return client;\n}\n\nexport async function refreshSession(): Promise<void> {\n const client = getSupabaseClient();\n const refreshToken = credentials.refreshToken;\n\n if (!refreshToken) {\n throw new Error('No refresh token found. Run `playtagon login` first.');\n }\n\n const { data, error } = await client.auth.refreshSession({\n refresh_token: refreshToken,\n });\n\n if (error) {\n credentials.clear();\n throw new Error(`Failed to refresh session: ${error.message}`);\n }\n\n if (data.session) {\n credentials.save({\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at\n ? data.session.expires_at * 1000\n : Date.now() + 3600 * 1000,\n });\n }\n}\n\nexport async function loginWithEmail(\n email: string,\n password: string\n): Promise<void> {\n const client = getSupabaseClient();\n\n const { data, error } = await client.auth.signInWithPassword({\n email,\n password,\n });\n\n if (error) {\n throw new Error(`Login failed: ${error.message}`);\n }\n\n if (!data.session) {\n throw new Error('Login failed: No session returned');\n }\n\n credentials.save({\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at\n ? data.session.expires_at * 1000\n : Date.now() + 3600 * 1000,\n userId: data.user?.id,\n email: data.user?.email,\n });\n}\n\nexport interface OAuthLoginResult {\n success: boolean;\n error?: string;\n}\n\nexport async function loginWithBrowser(): Promise<OAuthLoginResult> {\n return new Promise((resolve) => {\n const port = 54321;\n const redirectUri = `http://localhost:${port}/callback`;\n const codeVerifier = generateCodeVerifier();\n const codeChallenge = generateCodeChallenge(codeVerifier);\n\n // Build the Supabase auth URL with PKCE\n const authUrl = new URL(`${config.supabaseUrl}/auth/v1/authorize`);\n authUrl.searchParams.set('provider', 'google'); // or use generic PKCE\n authUrl.searchParams.set('redirect_to', redirectUri);\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n // For email login via browser, we use the Supabase hosted UI\n const loginUrl = new URL(`${config.supabaseUrl}/auth/v1/authorize`);\n // Use the platform's auth page instead\n const platformAuthUrl = `https://platform.playtagon.com/cli-auth?redirect_uri=${encodeURIComponent(redirectUri)}&code_challenge=${codeChallenge}`;\n\n let server: http.Server | null = null;\n let timeoutId: NodeJS.Timeout | null = null;\n\n const cleanup = () => {\n if (timeoutId) clearTimeout(timeoutId);\n if (server) {\n server.close();\n server = null;\n }\n };\n\n // Create local server to receive the callback\n server = http.createServer(async (req, res) => {\n const url = new URL(req.url || '/', `http://localhost:${port}`);\n\n if (url.pathname === '/callback') {\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n const errorDescription = url.searchParams.get('error_description');\n\n if (error) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(errorDescription || error));\n cleanup();\n resolve({ success: false, error: errorDescription || error });\n return;\n }\n\n if (code) {\n try {\n // Exchange code for session\n const client = getSupabaseClient();\n const { data, error: exchangeError } = await client.auth.exchangeCodeForSession(code);\n\n if (exchangeError || !data.session) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(exchangeError?.message || 'Failed to exchange code'));\n cleanup();\n resolve({ success: false, error: exchangeError?.message || 'Failed to exchange code' });\n return;\n }\n\n // Save credentials\n credentials.save({\n accessToken: data.session.access_token,\n refreshToken: data.session.refresh_token,\n expiresAt: data.session.expires_at\n ? data.session.expires_at * 1000\n : Date.now() + 3600 * 1000,\n userId: data.user?.id,\n email: data.user?.email,\n });\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessHtml(data.user?.email || ''));\n cleanup();\n resolve({ success: true });\n } catch (err) {\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml(err instanceof Error ? err.message : 'Unknown error'));\n cleanup();\n resolve({ success: false, error: err instanceof Error ? err.message : 'Unknown error' });\n }\n return;\n }\n\n // Handle tokens directly in hash (for implicit flow fallback)\n const accessToken = url.searchParams.get('access_token');\n const refreshToken = url.searchParams.get('refresh_token');\n\n if (accessToken) {\n credentials.save({\n accessToken,\n refreshToken: refreshToken || undefined,\n expiresAt: Date.now() + 3600 * 1000,\n });\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessHtml(''));\n cleanup();\n resolve({ success: true });\n return;\n }\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getErrorHtml('No authorization code received'));\n cleanup();\n resolve({ success: false, error: 'No authorization code received' });\n } else {\n res.writeHead(404);\n res.end('Not found');\n }\n });\n\n server.listen(port, () => {\n logger.info(`Waiting for authentication on port ${port}...`);\n });\n\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n cleanup();\n resolve({ success: false, error: `Port ${port} is already in use. Please close any applications using it.` });\n } else {\n cleanup();\n resolve({ success: false, error: err.message });\n }\n });\n\n // Timeout after 5 minutes\n timeoutId = setTimeout(() => {\n cleanup();\n resolve({ success: false, error: 'Authentication timed out. Please try again.' });\n }, 5 * 60 * 1000);\n });\n}\n\nexport function getAuthUrl(): string {\n const port = 54321;\n const redirectUri = `http://localhost:${port}/callback`;\n\n // Use platform's auth page which handles the Supabase flow\n return `https://platform.playtagon.com/cli-auth?redirect_uri=${encodeURIComponent(redirectUri)}`;\n}\n\nfunction getSuccessHtml(email: string): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Login Successful - Playtagon CLI</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n color: #fff;\n }\n .container {\n text-align: center;\n padding: 40px;\n background: rgba(255,255,255,0.1);\n border-radius: 16px;\n backdrop-filter: blur(10px);\n }\n .checkmark {\n font-size: 64px;\n margin-bottom: 20px;\n }\n h1 { margin: 0 0 10px; font-size: 24px; }\n p { margin: 0; opacity: 0.8; }\n .email { font-weight: bold; color: #4ade80; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"checkmark\">✓</div>\n <h1>Login Successful!</h1>\n <p>You're now logged in${email ? ` as <span class=\"email\">${email}</span>` : ''}.</p>\n <p style=\"margin-top: 20px; opacity: 0.6;\">You can close this window and return to your terminal.</p>\n </div>\n</body>\n</html>`;\n}\n\nfunction getErrorHtml(error: string): string {\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Login Failed - Playtagon CLI</title>\n <style>\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 100vh;\n margin: 0;\n background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);\n color: #fff;\n }\n .container {\n text-align: center;\n padding: 40px;\n background: rgba(255,255,255,0.1);\n border-radius: 16px;\n backdrop-filter: blur(10px);\n }\n .icon {\n font-size: 64px;\n margin-bottom: 20px;\n }\n h1 { margin: 0 0 10px; font-size: 24px; color: #f87171; }\n p { margin: 0; opacity: 0.8; }\n .error { color: #fca5a5; font-family: monospace; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"icon\">✗</div>\n <h1>Login Failed</h1>\n <p class=\"error\">${error}</p>\n <p style=\"margin-top: 20px; opacity: 0.6;\">Please return to your terminal and try again.</p>\n </div>\n</body>\n</html>`;\n}\n\nexport function logout(): void {\n credentials.clear();\n}\n\nexport async function getCurrentUser(): Promise<{\n id: string;\n email: string;\n studios: Array<{ id: string; name: string; slug: string }>;\n} | null> {\n try {\n const client = await getAuthenticatedClient();\n\n const {\n data: { user },\n error,\n } = await client.auth.getUser();\n\n if (error || !user) {\n return null;\n }\n\n // Fetch user's studios\n const { data: memberships } = await client.from('studio_members').select(`\n studio:studios (\n id,\n name,\n slug\n )\n `)\n .eq('user_id', user.id);\n\n const studios =\n memberships\n ?.map((m) => m.studio as unknown as { id: string; name: string; slug: string })\n .filter(Boolean) || [];\n\n return {\n id: user.id,\n email: user.email || '',\n studios,\n };\n } catch {\n return null;\n }\n}\n\nexport function getAccessToken(): string | undefined {\n return credentials.accessToken;\n}\n","import Conf from 'conf';\n\nexport interface PlaytagonConfig {\n supabaseUrl?: string;\n supabaseAnonKey?: string;\n defaultStudio?: string;\n defaultGame?: string;\n}\n\nexport interface PlaytagonCredentials {\n accessToken?: string;\n refreshToken?: string;\n expiresAt?: number;\n userId?: string;\n email?: string;\n}\n\n// Default Supabase config - can be overridden via environment variables\nconst DEFAULT_SUPABASE_URL = process.env.PLAYTAGON_SUPABASE_URL || 'https://yutpzwvdpktvblylglwz.supabase.co';\nconst DEFAULT_SUPABASE_ANON_KEY = process.env.PLAYTAGON_SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inl1dHB6d3ZkcGt0dmJseWxnbHd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzU4MjMwNjcsImV4cCI6MjA1MTM5OTA2N30.xyNgJxBLPG3W27Lv6oCctRz-DJLy0jxiHCm7W1yQ4j0';\n\n// Config store for settings\nconst configStore = new Conf<PlaytagonConfig>({\n projectName: 'playtagon',\n configFileMode: 0o600,\n defaults: {\n supabaseUrl: DEFAULT_SUPABASE_URL,\n supabaseAnonKey: DEFAULT_SUPABASE_ANON_KEY,\n },\n});\n\n// Separate store for credentials (more sensitive)\nconst credentialsStore = new Conf<PlaytagonCredentials>({\n projectName: 'playtagon',\n configName: 'credentials',\n configFileMode: 0o600,\n});\n\nexport const config = {\n get supabaseUrl(): string {\n return configStore.get('supabaseUrl') || DEFAULT_SUPABASE_URL;\n },\n\n get supabaseAnonKey(): string {\n return configStore.get('supabaseAnonKey') || DEFAULT_SUPABASE_ANON_KEY;\n },\n\n get defaultStudio(): string | undefined {\n return configStore.get('defaultStudio');\n },\n\n set defaultStudio(value: string | undefined) {\n if (value) {\n configStore.set('defaultStudio', value);\n } else {\n configStore.delete('defaultStudio');\n }\n },\n\n get defaultGame(): string | undefined {\n return configStore.get('defaultGame');\n },\n\n set defaultGame(value: string | undefined) {\n if (value) {\n configStore.set('defaultGame', value);\n } else {\n configStore.delete('defaultGame');\n }\n },\n\n // Get full config\n getAll(): PlaytagonConfig {\n return configStore.store;\n },\n\n // Get config file path\n get path(): string {\n return configStore.path;\n },\n};\n\nexport const credentials = {\n get accessToken(): string | undefined {\n return credentialsStore.get('accessToken');\n },\n\n get refreshToken(): string | undefined {\n return credentialsStore.get('refreshToken');\n },\n\n get expiresAt(): number | undefined {\n return credentialsStore.get('expiresAt');\n },\n\n get userId(): string | undefined {\n return credentialsStore.get('userId');\n },\n\n get email(): string | undefined {\n return credentialsStore.get('email');\n },\n\n isLoggedIn(): boolean {\n return !!credentialsStore.get('accessToken');\n },\n\n isExpired(): boolean {\n const expiresAt = credentialsStore.get('expiresAt');\n if (!expiresAt) return true;\n // Consider expired if less than 5 minutes remaining\n return Date.now() > expiresAt - 5 * 60 * 1000;\n },\n\n save(creds: PlaytagonCredentials): void {\n if (creds.accessToken) credentialsStore.set('accessToken', creds.accessToken);\n if (creds.refreshToken) credentialsStore.set('refreshToken', creds.refreshToken);\n if (creds.expiresAt) credentialsStore.set('expiresAt', creds.expiresAt);\n if (creds.userId) credentialsStore.set('userId', creds.userId);\n if (creds.email) credentialsStore.set('email', creds.email);\n },\n\n clear(): void {\n credentialsStore.clear();\n },\n\n // Get credentials file path\n get path(): string {\n return credentialsStore.path;\n },\n};\n","import chalk from 'chalk';\n\nexport const logger = {\n info: (message: string) => {\n console.log(chalk.blue('info'), message);\n },\n\n success: (message: string) => {\n console.log(chalk.green('success'), message);\n },\n\n warn: (message: string) => {\n console.log(chalk.yellow('warn'), message);\n },\n\n error: (message: string) => {\n console.log(chalk.red('error'), message);\n },\n\n debug: (message: string) => {\n if (process.env.DEBUG) {\n console.log(chalk.gray('debug'), message);\n }\n },\n\n // Styled output for specific contexts\n file: (filename: string) => chalk.cyan(filename),\n value: (val: string | number) => chalk.yellow(val),\n command: (cmd: string) => chalk.bold.white(cmd),\n url: (url: string) => chalk.underline.blue(url),\n\n // Section headers\n header: (title: string) => {\n console.log();\n console.log(chalk.bold.white(title));\n console.log(chalk.gray('─'.repeat(title.length)));\n },\n\n // List item\n item: (label: string, value: string) => {\n console.log(` ${chalk.gray(label + ':')} ${value}`);\n },\n\n // Validation result formatting\n validationError: (message: string, detail?: string) => {\n console.log(` ${chalk.red('✗')} ${message}`);\n if (detail) {\n console.log(` ${chalk.gray(detail)}`);\n }\n },\n\n validationSuccess: (message: string) => {\n console.log(` ${chalk.green('✓')} ${message}`);\n },\n\n validationWarn: (message: string) => {\n console.log(` ${chalk.yellow('!')} ${message}`);\n },\n};\n","import { Command } from 'commander';\nimport { logout } from '../lib/auth.js';\nimport { credentials } from '../lib/config.js';\nimport { logger } from '../utils/logger.js';\n\nexport const logoutCommand = new Command('logout')\n .description('Sign out of Playtagon')\n .action(() => {\n if (!credentials.isLoggedIn()) {\n logger.info('Not currently logged in.');\n return;\n }\n\n const email = credentials.email;\n logout();\n\n if (email) {\n logger.success(`Logged out from ${logger.value(email)}`);\n } else {\n logger.success('Logged out successfully');\n }\n });\n","import { Command } from 'commander';\nimport ora from 'ora';\nimport { getCurrentUser } from '../lib/auth.js';\nimport { credentials } from '../lib/config.js';\nimport { logger } from '../utils/logger.js';\n\nexport const whoamiCommand = new Command('whoami')\n .description('Show current logged in user')\n .action(async () => {\n if (!credentials.isLoggedIn()) {\n logger.info('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} to authenticate.`);\n return;\n }\n\n const spinner = ora('Fetching user info...').start();\n\n try {\n const user = await getCurrentUser();\n\n if (!user) {\n spinner.fail('Session expired');\n logger.info(`Run ${logger.command('playtagon login')} to re-authenticate.`);\n return;\n }\n\n spinner.stop();\n\n logger.header('Current User');\n logger.item('ID', user.id);\n logger.item('Email', user.email);\n\n if (user.studios.length > 0) {\n console.log();\n logger.header('Studios');\n for (const studio of user.studios) {\n console.log(` ${studio.name} (${logger.value(studio.slug)})`);\n }\n } else {\n console.log();\n logger.warn('No studios found. Join or create a studio in the platform.');\n }\n } catch (error) {\n spinner.fail('Failed to fetch user info');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n","import { Command } from 'commander';\nimport { validateCommand } from './validate.js';\nimport { uploadCommand } from './upload.js';\nimport { presetCommand } from './preset.js';\n\nexport const spineCommand = new Command('spine')\n .description('Manage Spine animation assets')\n .addCommand(validateCommand)\n .addCommand(uploadCommand)\n .addCommand(presetCommand);\n","import { Command } from 'commander';\nimport * as path from 'node:path';\nimport ora from 'ora';\nimport { validateDirectory, type BatchValidation } from '../../lib/spine-validator.js';\nimport { formatFileSize } from '../../utils/files.js';\nimport { logger } from '../../utils/logger.js';\n\nexport const validateCommand = new Command('validate')\n .description('Validate Spine files before uploading')\n .argument('<directory>', 'Directory containing Spine files')\n .option('--batch', 'Enable batch mode for multiple skeletons sharing atlas')\n .action(async (directory: string, options: { batch?: boolean }) => {\n const absolutePath = path.resolve(directory);\n\n const spinner = ora(`Validating ${logger.file(directory)}...`).start();\n\n try {\n const result = validateDirectory(absolutePath, options.batch);\n spinner.stop();\n\n printValidationResult(result, directory);\n\n if (!result.isValid) {\n process.exit(1);\n }\n } catch (error) {\n spinner.fail('Validation failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\nfunction printValidationResult(result: BatchValidation, directory: string): void {\n console.log();\n logger.header(`Validation: ${directory}`);\n\n // Summary info\n if (result.isBatchMode) {\n console.log(` Mode: ${logger.value('Batch')} (${result.assets.length} skeletons)`);\n if (result.sharedAtlas) {\n console.log(` Shared Atlas: ${logger.file(path.basename(result.sharedAtlas))}`);\n console.log(` Shared Textures: ${result.sharedTextures.length}`);\n }\n } else if (result.assets.length > 0) {\n console.log(` Mode: ${logger.value('Single')}`);\n }\n\n console.log(` Total Size: ${logger.value(formatFileSize(result.totalSize))}`);\n console.log();\n\n // Print each asset\n for (const asset of result.assets) {\n const statusIcon = asset.isValid ? '✓' : '✗';\n const statusColor = asset.isValid ? 'green' : 'red';\n\n console.log(\n ` ${statusIcon} ${logger.file(asset.skeleton.name)}`\n );\n console.log(` Spine: ${asset.skeleton.spineVersion}`);\n console.log(` Animations: ${asset.skeleton.animations.length}`);\n console.log(` Skins: ${asset.skeleton.skins.length}`);\n\n if (asset.atlasPath) {\n console.log(` Atlas: ${path.basename(asset.atlasPath)}`);\n console.log(` Textures: ${asset.texturePaths.length}`);\n }\n }\n\n console.log();\n\n // Print all issues\n const errors = result.issues.filter((i) => i.type === 'error');\n const warnings = result.issues.filter((i) => i.type === 'warning');\n const infos = result.issues.filter((i) => i.type === 'info');\n\n // Info messages\n for (const info of infos) {\n logger.info(info.message);\n }\n\n // Warnings\n if (warnings.length > 0) {\n console.log();\n for (const warning of warnings) {\n logger.validationWarn(\n warning.file ? `${warning.file}: ${warning.message}` : warning.message\n );\n if ((warning as { detail?: string }).detail) {\n console.log(` ${(warning as { detail: string }).detail}`);\n }\n }\n }\n\n // Errors\n if (errors.length > 0) {\n console.log();\n for (const error of errors) {\n logger.validationError(\n error.file ? `${error.file}: ${error.message}` : error.message,\n (error as { detail?: string }).detail\n );\n }\n }\n\n // Final status\n console.log();\n if (result.isValid) {\n logger.success('Validation passed! Ready for upload.');\n } else {\n logger.error(`Validation failed with ${errors.length} error(s).`);\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n discoverSpineFiles,\n matchSkeletonToAtlas,\n parseAtlasTextures,\n getFileSize,\n getTotalSize,\n formatFileSize,\n type DiscoveredFiles,\n} from '../utils/files.js';\n\n// Validation rules - keep in sync with server and platform\nexport const SPINE_VALIDATION_RULES = {\n supportedVersions: ['4.0', '4.1', '4.2', '4.3'] as const,\n maxTextureSize: 4096, // pixels per dimension\n maxTotalSize: 50 * 1024 * 1024, // 50MB\n maxTextureCount: 8,\n slugPattern: /^[a-z0-9-]+$/,\n animationNamePattern: /^[a-zA-Z0-9_-]+$/,\n} as const;\n\nexport interface ValidationError {\n type: 'error';\n message: string;\n file?: string;\n detail?: string;\n}\n\nexport interface ValidationWarning {\n type: 'warning';\n message: string;\n file?: string;\n detail?: string;\n}\n\nexport interface ValidationInfo {\n type: 'info';\n message: string;\n}\n\nexport type ValidationIssue = ValidationError | ValidationWarning | ValidationInfo;\n\nexport interface SkeletonInfo {\n path: string;\n name: string;\n spineVersion: string;\n animations: string[];\n skins: string[];\n hasEvents: boolean;\n boneCount: number;\n slotCount: number;\n}\n\nexport interface AssetValidation {\n skeleton: SkeletonInfo;\n atlasPath: string | null;\n texturePaths: string[];\n totalSize: number;\n issues: ValidationIssue[];\n isValid: boolean;\n}\n\nexport interface BatchValidation {\n assets: AssetValidation[];\n sharedAtlas: string | null;\n sharedTextures: string[];\n totalSize: number;\n issues: ValidationIssue[];\n isValid: boolean;\n isBatchMode: boolean;\n}\n\nexport function validateDirectory(\n directory: string,\n batchMode = false\n): BatchValidation {\n const discovered = discoverSpineFiles(directory);\n const issues: ValidationIssue[] = [];\n\n // Check if we found any skeleton files\n if (discovered.skeletons.length === 0) {\n issues.push({\n type: 'error',\n message: 'No Spine skeleton files found',\n detail: 'Expected .json (Spine skeleton) or .skel files',\n });\n return {\n assets: [],\n sharedAtlas: null,\n sharedTextures: [],\n totalSize: 0,\n issues,\n isValid: false,\n isBatchMode: false,\n };\n }\n\n // Determine if this is batch mode (multiple skeletons sharing atlas)\n const isBatchMode = batchMode || discovered.skeletons.length > 1;\n\n if (isBatchMode && discovered.skeletons.length > 1) {\n return validateBatch(discovered, issues);\n } else {\n return validateSingle(discovered, issues);\n }\n}\n\nfunction validateSingle(\n discovered: DiscoveredFiles,\n issues: ValidationIssue[]\n): BatchValidation {\n const skeletonPath = discovered.skeletons[0];\n const skeleton = parseSkeletonFile(skeletonPath);\n\n if (!skeleton) {\n issues.push({\n type: 'error',\n message: 'Failed to parse skeleton file',\n file: path.basename(skeletonPath),\n });\n return {\n assets: [],\n sharedAtlas: null,\n sharedTextures: [],\n totalSize: 0,\n issues,\n isValid: false,\n isBatchMode: false,\n };\n }\n\n // Validate spine version\n const versionIssues = validateSpineVersion(skeleton);\n issues.push(...versionIssues);\n\n // Match atlas\n const atlasPath = matchSkeletonToAtlas(skeletonPath, discovered.atlases);\n if (!atlasPath) {\n if (discovered.atlases.length === 0) {\n issues.push({\n type: 'error',\n message: 'No atlas file found',\n detail: 'Expected .atlas file',\n });\n } else {\n issues.push({\n type: 'warning',\n message: 'Could not match skeleton to atlas',\n detail: `Found atlases: ${discovered.atlases.map((a) => path.basename(a)).join(', ')}`,\n });\n }\n }\n\n // Get textures from atlas\n let texturePaths: string[] = [];\n if (atlasPath) {\n texturePaths = parseAtlasTextures(atlasPath);\n const textureIssues = validateTextures(texturePaths);\n issues.push(...textureIssues);\n }\n\n // Check for orphan textures\n const orphanTextures = discovered.textures.filter(\n (t) => !texturePaths.includes(t)\n );\n if (orphanTextures.length > 0) {\n issues.push({\n type: 'warning',\n message: `${orphanTextures.length} texture(s) not referenced in atlas`,\n detail: orphanTextures.map((t) => path.basename(t)).join(', '),\n });\n }\n\n // Calculate total size\n const allFiles = [skeletonPath, atlasPath, ...texturePaths].filter(\n Boolean\n ) as string[];\n const totalSize = getTotalSize(allFiles);\n\n // Validate total size\n if (totalSize > SPINE_VALIDATION_RULES.maxTotalSize) {\n issues.push({\n type: 'error',\n message: `Total size ${formatFileSize(totalSize)} exceeds ${formatFileSize(SPINE_VALIDATION_RULES.maxTotalSize)} limit`,\n });\n }\n\n // Validate animation names\n const animationIssues = validateAnimationNames(skeleton.animations);\n issues.push(...animationIssues);\n\n const hasErrors = issues.some((i) => i.type === 'error');\n\n const asset: AssetValidation = {\n skeleton,\n atlasPath,\n texturePaths,\n totalSize,\n issues: issues.filter(\n (i) =>\n (i as ValidationError | ValidationWarning).file === undefined ||\n (i as ValidationError | ValidationWarning).file === path.basename(skeletonPath)\n ),\n isValid: !hasErrors,\n };\n\n return {\n assets: [asset],\n sharedAtlas: null,\n sharedTextures: [],\n totalSize,\n issues,\n isValid: !hasErrors,\n isBatchMode: false,\n };\n}\n\nfunction validateBatch(\n discovered: DiscoveredFiles,\n issues: ValidationIssue[]\n): BatchValidation {\n const assets: AssetValidation[] = [];\n\n // In batch mode, we expect a shared atlas\n let sharedAtlas: string | null = null;\n let sharedTextures: string[] = [];\n\n if (discovered.atlases.length === 1) {\n sharedAtlas = discovered.atlases[0];\n sharedTextures = parseAtlasTextures(sharedAtlas);\n\n issues.push({\n type: 'info',\n message: `Batch mode: ${discovered.skeletons.length} skeletons sharing 1 atlas`,\n });\n } else if (discovered.atlases.length === 0) {\n issues.push({\n type: 'error',\n message: 'No atlas file found for batch upload',\n });\n } else {\n issues.push({\n type: 'warning',\n message: `Multiple atlases found (${discovered.atlases.length}). Batch mode works best with shared atlas.`,\n });\n }\n\n // Validate each skeleton\n for (const skeletonPath of discovered.skeletons) {\n const skeleton = parseSkeletonFile(skeletonPath);\n\n if (!skeleton) {\n issues.push({\n type: 'error',\n message: 'Failed to parse skeleton file',\n file: path.basename(skeletonPath),\n });\n continue;\n }\n\n // Validate spine version\n const versionIssues = validateSpineVersion(skeleton);\n issues.push(...versionIssues);\n\n // Validate animation names\n const animationIssues = validateAnimationNames(skeleton.animations);\n issues.push(...animationIssues);\n\n // Each skeleton uses the shared atlas\n const atlasPath = sharedAtlas || matchSkeletonToAtlas(skeletonPath, discovered.atlases);\n const texturePaths = atlasPath ? parseAtlasTextures(atlasPath) : [];\n\n // Calculate size for this asset (skeleton only, atlas counted once at the end)\n const skeletonSize = getFileSize(skeletonPath);\n\n assets.push({\n skeleton,\n atlasPath,\n texturePaths,\n totalSize: skeletonSize,\n issues: [],\n isValid: true,\n });\n }\n\n // Validate shared textures\n if (sharedTextures.length > 0) {\n const textureIssues = validateTextures(sharedTextures);\n issues.push(...textureIssues);\n }\n\n // Calculate total size (atlas + textures counted once)\n const skeletonSizes = assets.reduce((sum, a) => sum + a.totalSize, 0);\n const atlasSize = sharedAtlas ? getFileSize(sharedAtlas) : 0;\n const textureSize = getTotalSize(sharedTextures);\n const totalSize = skeletonSizes + atlasSize + textureSize;\n\n // Validate total size\n if (totalSize > SPINE_VALIDATION_RULES.maxTotalSize) {\n issues.push({\n type: 'error',\n message: `Total batch size ${formatFileSize(totalSize)} exceeds ${formatFileSize(SPINE_VALIDATION_RULES.maxTotalSize)} limit`,\n });\n }\n\n const hasErrors = issues.some((i) => i.type === 'error');\n\n return {\n assets,\n sharedAtlas,\n sharedTextures,\n totalSize,\n issues,\n isValid: !hasErrors,\n isBatchMode: true,\n };\n}\n\nfunction parseSkeletonFile(filePath: string): SkeletonInfo | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const json = JSON.parse(content);\n\n if (!json.skeleton?.spine) {\n return null;\n }\n\n const name = path.basename(filePath, path.extname(filePath));\n const spineVersion = json.skeleton.spine;\n const animations = json.animations ? Object.keys(json.animations) : [];\n const skins = json.skins\n ? Array.isArray(json.skins)\n ? json.skins.map((s: { name?: string }) => s.name || 'default')\n : Object.keys(json.skins)\n : ['default'];\n const hasEvents = !!json.events && Object.keys(json.events).length > 0;\n const boneCount = json.bones ? json.bones.length : 0;\n const slotCount = json.slots ? json.slots.length : 0;\n\n return {\n path: filePath,\n name,\n spineVersion,\n animations,\n skins,\n hasEvents,\n boneCount,\n slotCount,\n };\n } catch {\n return null;\n }\n}\n\nfunction validateSpineVersion(skeleton: SkeletonInfo): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Extract major.minor version\n const version = skeleton.spineVersion;\n const majorMinor = version.match(/^(\\d+\\.\\d+)/)?.[1];\n\n if (\n !majorMinor ||\n !SPINE_VALIDATION_RULES.supportedVersions.includes(\n majorMinor as (typeof SPINE_VALIDATION_RULES.supportedVersions)[number]\n )\n ) {\n issues.push({\n type: 'error',\n message: `Unsupported Spine version: ${version}`,\n file: path.basename(skeleton.path),\n detail: `Supported versions: ${SPINE_VALIDATION_RULES.supportedVersions.join(', ')}`,\n });\n }\n\n return issues;\n}\n\nfunction validateAnimationNames(animations: string[]): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n for (const name of animations) {\n if (!SPINE_VALIDATION_RULES.animationNamePattern.test(name)) {\n issues.push({\n type: 'warning',\n message: `Animation name \"${name}\" contains invalid characters`,\n detail: 'Recommended: a-z, A-Z, 0-9, _, -',\n });\n }\n }\n\n return issues;\n}\n\nfunction validateTextures(texturePaths: string[]): ValidationIssue[] {\n const issues: ValidationIssue[] = [];\n\n // Check texture count\n if (texturePaths.length > SPINE_VALIDATION_RULES.maxTextureCount) {\n issues.push({\n type: 'error',\n message: `Too many textures: ${texturePaths.length}`,\n detail: `Maximum allowed: ${SPINE_VALIDATION_RULES.maxTextureCount}`,\n });\n }\n\n // Check each texture\n for (const texturePath of texturePaths) {\n if (!fs.existsSync(texturePath)) {\n issues.push({\n type: 'error',\n message: `Texture file not found: ${path.basename(texturePath)}`,\n });\n continue;\n }\n\n // Check file size (basic check - full dimension check would require image parsing)\n const size = getFileSize(texturePath);\n if (size > 20 * 1024 * 1024) {\n // 20MB per texture is suspicious\n issues.push({\n type: 'warning',\n message: `Large texture file: ${path.basename(texturePath)} (${formatFileSize(size)})`,\n detail: 'Consider optimizing texture size',\n });\n }\n }\n\n return issues;\n}\n\nexport function generateSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n}\n\nexport function validateSlug(slug: string): boolean {\n return SPINE_VALIDATION_RULES.slugPattern.test(slug);\n}\n\n// Export preset for Spine Editor\nexport const SPINE_EXPORT_PRESET = {\n class: 'export-json',\n name: 'Playtagon Platform',\n output: '{projectDir}/exports/{skeletonName}',\n extension: '.json',\n format: 'json',\n nonessential: false,\n cleanUp: true,\n warnings: true,\n packAtlas: true,\n packSource: 'attachments',\n packTarget: 'perskeleton',\n packSettings: {\n maxWidth: 4096,\n maxHeight: 4096,\n paddingX: 2,\n paddingY: 2,\n edgePadding: true,\n duplicatePadding: true,\n rotation: true,\n stripWhitespaceX: true,\n stripWhitespaceY: true,\n pot: false,\n filterMin: 'Linear',\n filterMag: 'Linear',\n premultiplyAlpha: true,\n },\n};\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport interface SpineFileSet {\n skeletonFile: string; // .json or .skel\n atlasFile: string | null; // .atlas\n textureFiles: string[]; // .png or .jpg\n}\n\nexport interface DiscoveredFiles {\n skeletons: string[];\n atlases: string[];\n textures: string[];\n}\n\n// File extensions\nconst SKELETON_EXTENSIONS = ['.json', '.skel'];\nconst ATLAS_EXTENSIONS = ['.atlas'];\nconst TEXTURE_EXTENSIONS = ['.png', '.jpg', '.jpeg'];\n\nexport function discoverSpineFiles(directory: string): DiscoveredFiles {\n const absolutePath = path.resolve(directory);\n\n if (!fs.existsSync(absolutePath)) {\n throw new Error(`Directory not found: ${directory}`);\n }\n\n if (!fs.statSync(absolutePath).isDirectory()) {\n throw new Error(`Not a directory: ${directory}`);\n }\n\n const files = fs.readdirSync(absolutePath);\n\n const skeletons: string[] = [];\n const atlases: string[] = [];\n const textures: string[] = [];\n\n for (const file of files) {\n const ext = path.extname(file).toLowerCase();\n const fullPath = path.join(absolutePath, file);\n\n // Skip directories\n if (fs.statSync(fullPath).isDirectory()) continue;\n\n if (SKELETON_EXTENSIONS.includes(ext)) {\n // For .json files, we need to verify it's actually a Spine skeleton\n if (ext === '.json') {\n if (isSpineSkeletonJson(fullPath)) {\n skeletons.push(fullPath);\n }\n } else {\n skeletons.push(fullPath);\n }\n } else if (ATLAS_EXTENSIONS.includes(ext)) {\n atlases.push(fullPath);\n } else if (TEXTURE_EXTENSIONS.includes(ext)) {\n textures.push(fullPath);\n }\n }\n\n return { skeletons, atlases, textures };\n}\n\nexport function isSpineSkeletonJson(filePath: string): boolean {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n const json = JSON.parse(content);\n // Spine skeleton files have a \"skeleton\" property with version info\n return json.skeleton && typeof json.skeleton.spine === 'string';\n } catch {\n return false;\n }\n}\n\nexport function matchSkeletonToAtlas(\n skeletonPath: string,\n atlases: string[]\n): string | null {\n const skeletonName = path.basename(skeletonPath, path.extname(skeletonPath));\n const skeletonDir = path.dirname(skeletonPath);\n\n // Try exact name match first\n for (const atlas of atlases) {\n const atlasName = path.basename(atlas, '.atlas');\n if (atlasName === skeletonName) {\n return atlas;\n }\n }\n\n // If only one atlas in same directory, use it\n const sameDirectoryAtlases = atlases.filter(\n (a) => path.dirname(a) === skeletonDir\n );\n if (sameDirectoryAtlases.length === 1) {\n return sameDirectoryAtlases[0];\n }\n\n return null;\n}\n\nexport function parseAtlasTextures(atlasPath: string): string[] {\n const content = fs.readFileSync(atlasPath, 'utf-8');\n const lines = content.split('\\n');\n const textures: string[] = [];\n const atlasDir = path.dirname(atlasPath);\n\n for (const line of lines) {\n const trimmed = line.trim();\n // Texture file names are on their own lines, usually end with .png\n if (trimmed.endsWith('.png') || trimmed.endsWith('.jpg')) {\n const texturePath = path.join(atlasDir, trimmed);\n if (fs.existsSync(texturePath)) {\n textures.push(texturePath);\n }\n }\n }\n\n return textures;\n}\n\nexport function getFileSize(filePath: string): number {\n return fs.statSync(filePath).size;\n}\n\nexport function getTotalSize(files: string[]): number {\n return files.reduce((total, file) => total + getFileSize(file), 0);\n}\n\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n}\n\nexport function readFileAsBuffer(filePath: string): Buffer {\n return fs.readFileSync(filePath);\n}\n\nexport function getBaseName(filePath: string): string {\n return path.basename(filePath);\n}\n\nexport function getExtension(filePath: string): string {\n return path.extname(filePath).toLowerCase();\n}\n","import { Command } from 'commander';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport ora from 'ora';\nimport { credentials, config } from '../../lib/config.js';\nimport { getAccessToken } from '../../lib/auth.js';\nimport { uploadSpineAsset, resolveStudio, resolveGame } from '../../lib/api.js';\nimport {\n validateDirectory,\n generateSlug,\n validateSlug,\n type BatchValidation,\n} from '../../lib/spine-validator.js';\nimport { formatFileSize, getBaseName } from '../../utils/files.js';\nimport { logger } from '../../utils/logger.js';\n\nexport const uploadCommand = new Command('upload')\n .description('Upload Spine files to Playtagon')\n .argument('<directory>', 'Directory containing Spine files')\n .option('-s, --studio <studio>', 'Studio ID or slug (uses default if set)')\n .option('-g, --game <game>', 'Game ID or slug (uses default if set)')\n .option('-n, --name <name>', 'Asset name (defaults to directory name)')\n .option('--slug <slug>', 'Custom slug for the asset')\n .option('--batch', 'Enable batch mode for multiple skeletons sharing atlas')\n .option('--dry-run', 'Validate only, do not upload')\n .option('--description <text>', 'Asset description')\n .option('--tags <tags>', 'Comma-separated tags')\n .action(async (directory: string, options) => {\n // Check authentication\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n // Use defaults from config if not provided\n const studioOption = options.studio || config.defaultStudio;\n const gameOption = options.game || config.defaultGame;\n\n if (!studioOption) {\n logger.error('Studio is required.');\n logger.info(`Either provide ${logger.command('--studio <slug>')} or set a default:`);\n logger.info(` ${logger.command('playtagon config --studio <slug>')}`);\n logger.info(` ${logger.command('playtagon setup spine-integration --studio <slug>')}`);\n process.exit(1);\n }\n\n const absolutePath = path.resolve(directory);\n\n // Validate directory first\n const spinner = ora('Validating files...').start();\n\n let validation: BatchValidation;\n try {\n validation = validateDirectory(absolutePath, options.batch);\n } catch (error) {\n spinner.fail('Validation failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n\n if (!validation.isValid) {\n spinner.fail('Validation failed');\n printValidationErrors(validation);\n process.exit(1);\n }\n\n spinner.succeed(`Validated ${validation.assets.length} asset(s)`);\n\n // Resolve studio\n spinner.start('Resolving studio...');\n const studio = await resolveStudio(studioOption);\n if (!studio) {\n spinner.fail('Studio not found');\n logger.error(`Studio \"${studioOption}\" not found or you don't have access.`);\n process.exit(1);\n }\n spinner.succeed(`Studio: ${studio.name}`);\n\n // Resolve game if provided\n let game = null;\n if (gameOption) {\n spinner.start('Resolving game...');\n game = await resolveGame(studio.id, gameOption);\n if (!game) {\n spinner.fail('Game not found');\n logger.error(`Game \"${gameOption}\" not found in studio \"${studio.name}\".`);\n process.exit(1);\n }\n spinner.succeed(`Game: ${game.name}`);\n }\n\n // Determine asset name\n const assetName = options.name || path.basename(absolutePath);\n\n // Generate or validate slug\n let slug = options.slug || generateSlug(assetName);\n if (!validateSlug(slug)) {\n logger.error(`Invalid slug: \"${slug}\". Use lowercase letters, numbers, and hyphens only.`);\n process.exit(1);\n }\n\n // Print upload summary\n console.log();\n logger.header('Upload Summary');\n logger.item('Directory', directory);\n logger.item('Studio', studio.name);\n if (game) logger.item('Game', game.name);\n logger.item('Name', assetName);\n logger.item('Slug', slug);\n logger.item('Size', formatFileSize(validation.totalSize));\n\n if (validation.isBatchMode) {\n logger.item('Mode', 'Batch');\n logger.item('Skeletons', String(validation.assets.length));\n }\n\n if (options.dryRun) {\n console.log();\n logger.success('Dry run complete. No files were uploaded.');\n return;\n }\n\n // Build FormData for upload\n console.log();\n spinner.start('Uploading...');\n\n try {\n const formData = await buildFormData(validation, {\n studioId: studio.id,\n gameId: game?.id,\n name: assetName,\n slug,\n description: options.description,\n tags: options.tags?.split(',').map((t: string) => t.trim()),\n batchMode: validation.isBatchMode,\n });\n\n const result = await uploadSpineAsset(formData);\n\n if (result.success) {\n spinner.succeed('Upload complete!');\n\n console.log();\n if (result.assets && result.assets.length > 1) {\n logger.header('Uploaded Assets');\n for (const asset of result.assets) {\n console.log(` ${logger.value(asset.name)} (${asset.slug})`);\n console.log(` ID: ${asset.id}`);\n console.log(` Status: ${asset.status}`);\n }\n } else if (result.asset) {\n logger.header('Uploaded Asset');\n logger.item('ID', result.asset.id);\n logger.item('Name', result.asset.name);\n logger.item('Slug', result.asset.slug);\n logger.item('Status', result.asset.status);\n }\n } else {\n spinner.fail('Upload failed');\n logger.error(result.error || 'Unknown error');\n process.exit(1);\n }\n } catch (error) {\n spinner.fail('Upload failed');\n logger.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\nasync function buildFormData(\n validation: BatchValidation,\n options: {\n studioId: string;\n gameId?: string;\n name: string;\n slug: string;\n description?: string;\n tags?: string[];\n batchMode: boolean;\n }\n): Promise<FormData> {\n const formData = new FormData();\n\n // Add metadata\n formData.append('studioId', options.studioId);\n if (options.gameId) formData.append('gameId', options.gameId);\n formData.append('name', options.name);\n formData.append('slug', options.slug);\n if (options.description) formData.append('description', options.description);\n if (options.tags) formData.append('tags', JSON.stringify(options.tags));\n if (options.batchMode) formData.append('batchMode', 'true');\n\n // Collect all unique files\n const filesAdded = new Set<string>();\n\n // Add skeleton files\n for (const asset of validation.assets) {\n const filePath = asset.skeleton.path;\n if (!filesAdded.has(filePath)) {\n const buffer = fs.readFileSync(filePath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(filePath));\n filesAdded.add(filePath);\n }\n }\n\n // Add shared atlas if exists\n if (validation.sharedAtlas && !filesAdded.has(validation.sharedAtlas)) {\n const buffer = fs.readFileSync(validation.sharedAtlas);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(validation.sharedAtlas));\n filesAdded.add(validation.sharedAtlas);\n }\n\n // Add shared textures\n for (const texturePath of validation.sharedTextures) {\n if (!filesAdded.has(texturePath)) {\n const buffer = fs.readFileSync(texturePath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(texturePath));\n filesAdded.add(texturePath);\n }\n }\n\n // Add individual asset files (atlas and textures for non-batch mode)\n for (const asset of validation.assets) {\n if (asset.atlasPath && !filesAdded.has(asset.atlasPath)) {\n const buffer = fs.readFileSync(asset.atlasPath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(asset.atlasPath));\n filesAdded.add(asset.atlasPath);\n }\n\n for (const texturePath of asset.texturePaths) {\n if (!filesAdded.has(texturePath)) {\n const buffer = fs.readFileSync(texturePath);\n const blob = new Blob([buffer]);\n formData.append('files', blob, getBaseName(texturePath));\n filesAdded.add(texturePath);\n }\n }\n }\n\n return formData;\n}\n\nfunction printValidationErrors(validation: BatchValidation): void {\n const errors = validation.issues.filter((i) => i.type === 'error');\n console.log();\n for (const error of errors) {\n logger.validationError(\n error.file ? `${error.file}: ${error.message}` : error.message,\n (error as { detail?: string }).detail\n );\n }\n console.log();\n logger.info(`Run ${logger.command('playtagon spine validate <dir>')} for full report.`);\n}\n","import { config } from './config.js';\nimport { getAccessToken } from './auth.js';\n\nconst API_BASE = config.supabaseUrl;\n\nexport interface UploadResponse {\n success: boolean;\n asset?: {\n id: string;\n name: string;\n slug: string;\n version: number;\n status: string;\n };\n assets?: Array<{\n id: string;\n name: string;\n slug: string;\n version: number;\n status: string;\n }>;\n error?: string;\n}\n\nexport interface StudioInfo {\n id: string;\n name: string;\n slug: string;\n}\n\nexport interface GameInfo {\n id: string;\n name: string;\n slug: string;\n}\n\nexport async function uploadSpineAsset(formData: FormData): Promise<UploadResponse> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const response = await fetch(`${API_BASE}/functions/v1/spine-upload`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n body: formData,\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n let errorMessage: string;\n try {\n const errorJson = JSON.parse(errorText);\n errorMessage = errorJson.error || errorJson.message || errorText;\n } catch {\n errorMessage = errorText;\n }\n throw new Error(`Upload failed: ${errorMessage}`);\n }\n\n return response.json();\n}\n\nexport async function getStudios(): Promise<StudioInfo[]> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const response = await fetch(\n `${API_BASE}/rest/v1/studio_members?select=studio:studios(id,name,slug)`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch studios');\n }\n\n const data = await response.json();\n return data.map((m: { studio: StudioInfo }) => m.studio).filter(Boolean);\n}\n\nexport async function getGames(studioId: string): Promise<GameInfo[]> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const response = await fetch(\n `${API_BASE}/rest/v1/games?studio_id=eq.${studioId}&select=id,name,slug`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n throw new Error('Failed to fetch games');\n }\n\n return response.json();\n}\n\nexport async function resolveStudio(studioIdOrSlug: string): Promise<StudioInfo | null> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n // Try by ID first, then by slug\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(\n studioIdOrSlug\n );\n\n const query = isUuid\n ? `id=eq.${studioIdOrSlug}`\n : `slug=eq.${studioIdOrSlug}`;\n\n const response = await fetch(\n `${API_BASE}/rest/v1/studios?${query}&select=id,name,slug`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n return data[0] || null;\n}\n\nexport async function resolveGame(\n studioId: string,\n gameIdOrSlug: string\n): Promise<GameInfo | null> {\n const token = getAccessToken();\n\n if (!token) {\n throw new Error('Not authenticated. Run `playtagon login` first.');\n }\n\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(\n gameIdOrSlug\n );\n\n const idQuery = isUuid\n ? `id=eq.${gameIdOrSlug}`\n : `slug=eq.${gameIdOrSlug}`;\n\n const response = await fetch(\n `${API_BASE}/rest/v1/games?studio_id=eq.${studioId}&${idQuery}&select=id,name,slug`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n apikey: config.supabaseAnonKey,\n },\n }\n );\n\n if (!response.ok) {\n return null;\n }\n\n const data = await response.json();\n return data[0] || null;\n}\n","import { Command } from 'commander';\nimport { SPINE_EXPORT_PRESET } from '../../lib/spine-validator.js';\nimport { logger } from '../../utils/logger.js';\n\nexport const presetCommand = new Command('preset')\n .description('Output Spine export preset JSON for consistent exports')\n .option('--pretty', 'Pretty print JSON (default: true)', true)\n .option('--compact', 'Output compact JSON')\n .action((options) => {\n const pretty = !options.compact;\n\n if (pretty) {\n // Print usage info to stderr so it doesn't interfere with piping\n console.error(logger.info('Spine Export Preset for Playtagon Platform'));\n console.error();\n console.error('Usage:');\n console.error(' playtagon spine preset > playtagon-export.json');\n console.error(' Import this file into Spine Editor as an export preset.');\n console.error();\n }\n\n // Output JSON to stdout for easy piping\n const json = pretty\n ? JSON.stringify(SPINE_EXPORT_PRESET, null, 2)\n : JSON.stringify(SPINE_EXPORT_PRESET);\n\n console.log(json);\n });\n","import { Command } from 'commander';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as os from 'node:os';\nimport ora from 'ora';\nimport { credentials, config } from '../lib/config.js';\nimport { getCurrentUser } from '../lib/auth.js';\nimport { getStudios, getGames } from '../lib/api.js';\nimport { SPINE_EXPORT_PRESET } from '../lib/spine-validator.js';\nimport { logger } from '../utils/logger.js';\n\nconst PLAYTAGON_DIR = path.join(os.homedir(), '.playtagon');\n\nexport const setupCommand = new Command('setup')\n .description('Set up integrations')\n .addCommand(spineIntegrationCommand());\n\nfunction spineIntegrationCommand(): Command {\n return new Command('spine-integration')\n .description('Set up automatic Spine Editor to Platform upload')\n .option('-s, --studio <studio>', 'Default studio for uploads')\n .option('-g, --game <game>', 'Default game for uploads')\n .option('--force', 'Overwrite existing setup files')\n .action(async (options) => {\n // Check authentication\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n const spinner = ora('Checking authentication...').start();\n\n // Verify auth and get user info\n const user = await getCurrentUser();\n if (!user) {\n spinner.fail('Session expired');\n logger.info(`Run ${logger.command('playtagon login')} to re-authenticate.`);\n process.exit(1);\n }\n\n spinner.succeed(`Logged in as ${user.email}`);\n\n // Get studios\n let studioSlug = options.studio;\n if (!studioSlug) {\n spinner.start('Fetching studios...');\n const studios = await getStudios();\n spinner.stop();\n\n if (studios.length === 0) {\n logger.error('No studios found. Join or create a studio first.');\n process.exit(1);\n }\n\n if (studios.length === 1) {\n studioSlug = studios[0].slug;\n logger.info(`Using studio: ${logger.value(studios[0].name)}`);\n } else {\n logger.header('Available Studios');\n studios.forEach((s, i) => {\n console.log(` ${i + 1}. ${s.name} (${logger.value(s.slug)})`);\n });\n console.log();\n logger.info(`Specify studio with ${logger.command('--studio <slug>')}`);\n logger.info(`Example: ${logger.command(`playtagon setup spine-integration --studio ${studios[0].slug}`)}`);\n process.exit(1);\n }\n }\n\n // Create .playtagon directory\n spinner.start('Creating setup files...');\n\n if (!fs.existsSync(PLAYTAGON_DIR)) {\n fs.mkdirSync(PLAYTAGON_DIR, { recursive: true, mode: 0o700 });\n }\n\n // Generate post-export scripts\n const scriptPaths = generatePostExportScripts(studioSlug, options.game, options.force);\n\n // Generate Spine export preset with postScript\n const presetPath = generateExportPreset(scriptPaths.sh, options.force);\n\n spinner.succeed('Setup complete!');\n\n // Print instructions\n console.log();\n logger.header('Setup Complete');\n console.log();\n logger.info('Files created:');\n console.log(` ${logger.file(scriptPaths.sh)}`);\n if (process.platform === 'win32') {\n console.log(` ${logger.file(scriptPaths.bat)}`);\n }\n console.log(` ${logger.file(presetPath)}`);\n\n console.log();\n logger.header('Next Steps');\n console.log();\n console.log(' 1. Open Spine Editor');\n console.log(' 2. Go to File → Export...');\n console.log(' 3. Click the gear icon (⚙) next to the preset dropdown');\n console.log(' 4. Click \"Import\" and select:');\n console.log(` ${logger.file(presetPath)}`);\n console.log(' 5. The preset \"Upload to Playtagon\" will be available');\n console.log();\n console.log(' Now when you export with this preset, files automatically');\n console.log(' upload to Playtagon Platform!');\n console.log();\n\n // Verify game if provided\n let gameSlug = options.game;\n if (gameSlug) {\n spinner.start('Verifying game...');\n const studios = await getStudios();\n const studio = studios.find((s) => s.slug === studioSlug);\n if (studio) {\n const games = await getGames(studio.id);\n const game = games.find(\n (g) => g.slug === gameSlug || g.id === gameSlug || g.name === gameSlug\n );\n if (!game) {\n spinner.fail('Game not found');\n logger.error(`Game \"${gameSlug}\" not found in studio \"${studioSlug}\".`);\n if (games.length > 0) {\n console.log();\n logger.info('Available games:');\n games.forEach((g) => {\n console.log(` ${logger.value(g.slug)} - ${g.name}`);\n });\n }\n process.exit(1);\n }\n gameSlug = game.slug; // Normalize to slug\n spinner.succeed(`Game: ${game.name}`);\n }\n }\n\n // Save defaults to config\n config.defaultStudio = studioSlug;\n if (gameSlug) {\n config.defaultGame = gameSlug;\n }\n\n if (gameSlug) {\n logger.info(`Uploads will go to: ${logger.value(studioSlug)} / ${logger.value(gameSlug)}`);\n } else {\n logger.info(`Uploads will go to studio: ${logger.value(studioSlug)}`);\n logger.info(`Add ${logger.command('--game <slug>')} to also set default game.`);\n }\n\n console.log();\n logger.info('Defaults saved. You can change them later with:');\n logger.info(` ${logger.command('playtagon config --studio <slug> --game <slug>')}`);\n });\n}\n\nfunction generatePostExportScripts(\n studioSlug: string,\n gameSlug?: string,\n force = false\n): { sh: string; bat: string } {\n const shPath = path.join(PLAYTAGON_DIR, 'upload.sh');\n const batPath = path.join(PLAYTAGON_DIR, 'upload.bat');\n\n // Build upload command\n let uploadCmd = `playtagon spine upload \"$1\" --studio ${studioSlug}`;\n if (gameSlug) {\n uploadCmd += ` --game ${gameSlug}`;\n }\n\n // Shell script (Mac/Linux)\n const shScript = `#!/bin/bash\n# Playtagon Spine Upload Script\n# Auto-generated by: playtagon setup spine-integration\n#\n# This script is called by Spine Editor after export.\n# It uploads the exported files to Playtagon Platform.\n\nset -e\n\nEXPORT_DIR=\"$1\"\n\nif [ -z \"$EXPORT_DIR\" ]; then\n echo \"Error: No export directory provided\"\n exit 1\nfi\n\necho \"Uploading to Playtagon...\"\n${uploadCmd}\n\n# macOS notification (optional)\nif command -v osascript &> /dev/null; then\n osascript -e 'display notification \"Spine export uploaded successfully\" with title \"Playtagon\"' 2>/dev/null || true\nfi\n\n# Linux notification (optional)\nif command -v notify-send &> /dev/null; then\n notify-send \"Playtagon\" \"Spine export uploaded successfully\" 2>/dev/null || true\nfi\n\necho \"Upload complete!\"\n`;\n\n // Batch script (Windows)\n let uploadCmdWin = `playtagon spine upload \"%~1\" --studio ${studioSlug}`;\n if (gameSlug) {\n uploadCmdWin += ` --game ${gameSlug}`;\n }\n\n const batScript = `@echo off\nREM Playtagon Spine Upload Script\nREM Auto-generated by: playtagon setup spine-integration\nREM\nREM This script is called by Spine Editor after export.\nREM It uploads the exported files to Playtagon Platform.\n\nset EXPORT_DIR=%~1\n\nif \"%EXPORT_DIR%\"==\"\" (\n echo Error: No export directory provided\n exit /b 1\n)\n\necho Uploading to Playtagon...\n${uploadCmdWin}\n\nREM Windows notification (PowerShell)\npowershell -Command \"& {Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.MessageBox]::Show('Spine export uploaded successfully', 'Playtagon', 'OK', 'Information')}\" 2>nul\n\necho Upload complete!\n`;\n\n // Write scripts\n if (!fs.existsSync(shPath) || force) {\n fs.writeFileSync(shPath, shScript, { mode: 0o755 });\n } else {\n logger.warn(`${shPath} already exists. Use --force to overwrite.`);\n }\n\n if (!fs.existsSync(batPath) || force) {\n fs.writeFileSync(batPath, batScript);\n }\n\n return { sh: shPath, bat: batPath };\n}\n\nfunction generateExportPreset(scriptPath: string, force = false): string {\n const presetPath = path.join(PLAYTAGON_DIR, 'playtagon-spine-preset.export.json');\n\n // Extend the base preset with postScript\n const preset = {\n ...SPINE_EXPORT_PRESET,\n name: 'Upload to Playtagon',\n postScript: process.platform === 'win32'\n ? `\"${scriptPath.replace('.sh', '.bat')}\" \"{output}\"`\n : `\"${scriptPath}\" \"{output}\"`,\n };\n\n if (!fs.existsSync(presetPath) || force) {\n fs.writeFileSync(presetPath, JSON.stringify(preset, null, 2));\n } else {\n logger.warn(`${presetPath} already exists. Use --force to overwrite.`);\n }\n\n return presetPath;\n}\n","import { Command } from 'commander';\nimport { config, credentials } from '../lib/config.js';\nimport { getStudios, getGames } from '../lib/api.js';\nimport { logger } from '../utils/logger.js';\nimport ora from 'ora';\n\nexport const configCommand = new Command('config')\n .description('View or set CLI configuration')\n .option('-s, --studio <studio>', 'Set default studio')\n .option('-g, --game <game>', 'Set default game')\n .option('--clear', 'Clear all default settings')\n .action(async (options) => {\n // If --clear flag is set, clear all defaults\n if (options.clear) {\n config.defaultStudio = undefined;\n config.defaultGame = undefined;\n logger.success('Default settings cleared.');\n return;\n }\n\n // If setting studio\n if (options.studio) {\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n const spinner = ora('Verifying studio...').start();\n const studios = await getStudios();\n const studio = studios.find(\n (s) => s.slug === options.studio || s.id === options.studio || s.name === options.studio\n );\n\n if (!studio) {\n spinner.fail('Studio not found');\n logger.error(`Studio \"${options.studio}\" not found or you don't have access.`);\n console.log();\n logger.info('Available studios:');\n studios.forEach((s) => {\n console.log(` ${logger.value(s.slug)} - ${s.name}`);\n });\n process.exit(1);\n }\n\n spinner.succeed(`Default studio set: ${studio.name} (${studio.slug})`);\n config.defaultStudio = studio.slug;\n }\n\n // If setting game\n if (options.game) {\n if (!credentials.isLoggedIn()) {\n logger.error('Not logged in.');\n logger.info(`Run ${logger.command('playtagon login')} first.`);\n process.exit(1);\n }\n\n const studioSlug = options.studio || config.defaultStudio;\n if (!studioSlug) {\n logger.error('Studio is required to set default game.');\n logger.info(`Either provide ${logger.command('--studio <slug>')} or set a default studio first.`);\n process.exit(1);\n }\n\n const spinner = ora('Verifying game...').start();\n const studios = await getStudios();\n const studio = studios.find(\n (s) => s.slug === studioSlug || s.id === studioSlug || s.name === studioSlug\n );\n\n if (!studio) {\n spinner.fail('Studio not found');\n process.exit(1);\n }\n\n const games = await getGames(studio.id);\n const game = games.find(\n (g) => g.slug === options.game || g.id === options.game || g.name === options.game\n );\n\n if (!game) {\n spinner.fail('Game not found');\n logger.error(`Game \"${options.game}\" not found in studio \"${studio.name}\".`);\n console.log();\n if (games.length > 0) {\n logger.info('Available games:');\n games.forEach((g) => {\n console.log(` ${logger.value(g.slug)} - ${g.name}`);\n });\n } else {\n logger.info('No games in this studio yet.');\n }\n process.exit(1);\n }\n\n spinner.succeed(`Default game set: ${game.name} (${game.slug})`);\n config.defaultGame = game.slug;\n }\n\n // If no options, show current config\n if (!options.studio && !options.game && !options.clear) {\n logger.header('Current Configuration');\n console.log();\n logger.item('Config file', config.path);\n console.log();\n\n const currentStudio = config.defaultStudio;\n const currentGame = config.defaultGame;\n\n if (currentStudio || currentGame) {\n logger.header('Default Upload Target');\n if (currentStudio) {\n logger.item('Studio', currentStudio);\n }\n if (currentGame) {\n logger.item('Game', currentGame);\n }\n console.log();\n logger.info('These defaults are used when --studio/--game are not provided.');\n console.log();\n logger.info(`To change: ${logger.command('playtagon config --studio <slug> --game <slug>')}`);\n logger.info(`To clear: ${logger.command('playtagon config --clear')}`);\n } else {\n logger.info('No default studio or game set.');\n console.log();\n logger.info(`Set defaults: ${logger.command('playtagon config --studio <slug> --game <slug>')}`);\n logger.info(`Or run setup: ${logger.command('playtagon setup spine-integration')}`);\n }\n }\n });\n"],"mappings":"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","names":["Command","resolve","resolve","Command","Command","Command","ora","Command","ora","Command","Command","path","ora","fs","path","Command","ora","Command","fs","path","ora","Command","ora","Command","Command","Command","Command","fs","path","ora","Command","ora","Command","ora","Command","Command"]}
|