playcademy 0.7.0 → 0.8.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 +374 -198
- package/dist/types.d.ts +19 -6
- package/dist/utils.js +23 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2513,6 +2513,16 @@ var CONFIG_FILE_NAMES = [
|
|
|
2513
2513
|
"playcademy.config.mjs"
|
|
2514
2514
|
];
|
|
2515
2515
|
|
|
2516
|
+
// src/constants/urls.ts
|
|
2517
|
+
var PLAYCADEMY_BASE_URLS = {
|
|
2518
|
+
staging: "https://hub.dev.playcademy.net",
|
|
2519
|
+
production: "https://hub.playcademy.com"
|
|
2520
|
+
};
|
|
2521
|
+
var GAME_BACKEND_DOMAINS = {
|
|
2522
|
+
staging: "staging.playcademy.gg",
|
|
2523
|
+
production: "playcademy.gg"
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2516
2526
|
// src/lib/auth/storage.ts
|
|
2517
2527
|
import { access, mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
|
|
2518
2528
|
import { homedir } from "node:os";
|
|
@@ -2521,7 +2531,7 @@ import { join } from "node:path";
|
|
|
2521
2531
|
// src/lib/core/context.ts
|
|
2522
2532
|
var context = {};
|
|
2523
2533
|
function setCliContext(ctx) {
|
|
2524
|
-
context = ctx;
|
|
2534
|
+
context = { ...context, ...ctx };
|
|
2525
2535
|
}
|
|
2526
2536
|
function getCliContext() {
|
|
2527
2537
|
return context;
|
|
@@ -2531,26 +2541,47 @@ function getWorkspace() {
|
|
|
2531
2541
|
}
|
|
2532
2542
|
|
|
2533
2543
|
// src/lib/core/config.ts
|
|
2544
|
+
function normalizeEnvironment(env) {
|
|
2545
|
+
if (!env) return null;
|
|
2546
|
+
const normalized = env.toLowerCase().trim();
|
|
2547
|
+
if (normalized === "staging" || normalized === "stage") {
|
|
2548
|
+
return "staging";
|
|
2549
|
+
}
|
|
2550
|
+
if (normalized === "production" || normalized === "prod") {
|
|
2551
|
+
return "production";
|
|
2552
|
+
}
|
|
2553
|
+
return null;
|
|
2554
|
+
}
|
|
2555
|
+
function ensureEnvironment(env, logger2) {
|
|
2556
|
+
if (env) {
|
|
2557
|
+
const normalized = normalizeEnvironment(env);
|
|
2558
|
+
if (!normalized) {
|
|
2559
|
+
logger2.newLine();
|
|
2560
|
+
logger2.error(`Invalid environment: "${env}"`);
|
|
2561
|
+
logger2.newLine();
|
|
2562
|
+
logger2.info("Valid environments: staging, production");
|
|
2563
|
+
logger2.newLine();
|
|
2564
|
+
process.exit(1);
|
|
2565
|
+
}
|
|
2566
|
+
setCliContext({ environment: normalized });
|
|
2567
|
+
}
|
|
2568
|
+
return getEnvironment();
|
|
2569
|
+
}
|
|
2534
2570
|
function getEnvironment() {
|
|
2535
2571
|
const context2 = getCliContext();
|
|
2536
2572
|
if (context2.environment) {
|
|
2537
2573
|
return context2.environment;
|
|
2538
2574
|
}
|
|
2539
2575
|
const env = process.env.PLAYCADEMY_ENV?.toLowerCase();
|
|
2540
|
-
if (env === "staging") {
|
|
2541
|
-
return
|
|
2576
|
+
if (env === "staging" || env === "production") {
|
|
2577
|
+
return env;
|
|
2542
2578
|
}
|
|
2543
|
-
return "
|
|
2579
|
+
return "staging";
|
|
2544
2580
|
}
|
|
2545
2581
|
function getBaseUrl() {
|
|
2546
2582
|
const explicitUrl = process.env.PLAYCADEMY_BASE_URL;
|
|
2547
|
-
if (explicitUrl)
|
|
2548
|
-
|
|
2549
|
-
}
|
|
2550
|
-
if (getEnvironment() === "staging") {
|
|
2551
|
-
return "https://hub.dev.playcademy.net";
|
|
2552
|
-
}
|
|
2553
|
-
return "https://hub.playcademy.com";
|
|
2583
|
+
if (explicitUrl) return explicitUrl;
|
|
2584
|
+
return PLAYCADEMY_BASE_URLS[getEnvironment()];
|
|
2554
2585
|
}
|
|
2555
2586
|
function getApiUrl(path) {
|
|
2556
2587
|
const baseUrl = getBaseUrl();
|
|
@@ -2573,6 +2604,12 @@ async function ensureAuthDir() {
|
|
|
2573
2604
|
const authDir = join(homedir(), ".playcademy");
|
|
2574
2605
|
await mkdir(authDir, { recursive: true });
|
|
2575
2606
|
}
|
|
2607
|
+
function createEmptyEnvironmentProfiles() {
|
|
2608
|
+
return {
|
|
2609
|
+
default: null,
|
|
2610
|
+
profiles: {}
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2576
2613
|
async function loadAuthStore() {
|
|
2577
2614
|
try {
|
|
2578
2615
|
await ensureAuthDir();
|
|
@@ -2583,8 +2620,8 @@ async function loadAuthStore() {
|
|
|
2583
2620
|
} catch {
|
|
2584
2621
|
}
|
|
2585
2622
|
return {
|
|
2586
|
-
|
|
2587
|
-
|
|
2623
|
+
staging: createEmptyEnvironmentProfiles(),
|
|
2624
|
+
production: createEmptyEnvironmentProfiles()
|
|
2588
2625
|
};
|
|
2589
2626
|
}
|
|
2590
2627
|
async function saveAuthStore(store) {
|
|
@@ -2601,45 +2638,79 @@ async function getCurrentProfile() {
|
|
|
2601
2638
|
};
|
|
2602
2639
|
}
|
|
2603
2640
|
const store = await loadAuthStore();
|
|
2641
|
+
const environment = getEnvironment();
|
|
2604
2642
|
const profileName = getProfileName();
|
|
2643
|
+
const envProfiles = store[environment];
|
|
2644
|
+
if (!envProfiles) {
|
|
2645
|
+
return null;
|
|
2646
|
+
}
|
|
2605
2647
|
if (profileName === "default") {
|
|
2606
|
-
return
|
|
2648
|
+
return envProfiles.default;
|
|
2607
2649
|
}
|
|
2608
|
-
return
|
|
2650
|
+
return envProfiles.profiles[profileName] || null;
|
|
2609
2651
|
}
|
|
2610
|
-
async function saveProfile(profileName, profile) {
|
|
2652
|
+
async function saveProfile(environment, profileName, profile) {
|
|
2611
2653
|
const store = await loadAuthStore();
|
|
2654
|
+
if (!store[environment]) {
|
|
2655
|
+
store[environment] = createEmptyEnvironmentProfiles();
|
|
2656
|
+
}
|
|
2657
|
+
const envProfiles = store[environment];
|
|
2612
2658
|
if (profileName === "default") {
|
|
2613
|
-
|
|
2659
|
+
envProfiles.default = profile;
|
|
2614
2660
|
} else {
|
|
2615
|
-
|
|
2661
|
+
envProfiles.profiles[profileName] = profile;
|
|
2616
2662
|
}
|
|
2617
2663
|
await saveAuthStore(store);
|
|
2618
2664
|
}
|
|
2619
|
-
async function removeProfile(profileName) {
|
|
2665
|
+
async function removeProfile(environment, profileName) {
|
|
2620
2666
|
const store = await loadAuthStore();
|
|
2667
|
+
if (!store[environment]) {
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
const envProfiles = store[environment];
|
|
2621
2671
|
if (profileName === "default") {
|
|
2622
|
-
|
|
2672
|
+
envProfiles.default = null;
|
|
2623
2673
|
} else {
|
|
2624
|
-
delete
|
|
2674
|
+
delete envProfiles.profiles[profileName];
|
|
2625
2675
|
}
|
|
2626
2676
|
await saveAuthStore(store);
|
|
2627
2677
|
}
|
|
2628
|
-
async function getProfile(profileName) {
|
|
2678
|
+
async function getProfile(environment, profileName) {
|
|
2629
2679
|
const store = await loadAuthStore();
|
|
2680
|
+
const envProfiles = store[environment];
|
|
2681
|
+
if (!envProfiles) {
|
|
2682
|
+
return null;
|
|
2683
|
+
}
|
|
2630
2684
|
if (profileName === "default") {
|
|
2631
|
-
return
|
|
2685
|
+
return envProfiles.default;
|
|
2632
2686
|
}
|
|
2633
|
-
return
|
|
2687
|
+
return envProfiles.profiles[profileName] || null;
|
|
2634
2688
|
}
|
|
2635
2689
|
async function listProfiles() {
|
|
2636
2690
|
const store = await loadAuthStore();
|
|
2637
|
-
const
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2691
|
+
const profileMap = /* @__PURE__ */ new Map();
|
|
2692
|
+
const stagingProfiles = [];
|
|
2693
|
+
if (store.staging.default) {
|
|
2694
|
+
stagingProfiles.push("default");
|
|
2695
|
+
}
|
|
2696
|
+
stagingProfiles.push(...Object.keys(store.staging.profiles));
|
|
2697
|
+
profileMap.set("staging", stagingProfiles);
|
|
2698
|
+
const productionProfiles = [];
|
|
2699
|
+
if (store.production.default) {
|
|
2700
|
+
productionProfiles.push("default");
|
|
2701
|
+
}
|
|
2702
|
+
productionProfiles.push(...Object.keys(store.production.profiles));
|
|
2703
|
+
profileMap.set("production", productionProfiles);
|
|
2704
|
+
return profileMap;
|
|
2705
|
+
}
|
|
2706
|
+
async function getAuthenticatedEnvironments() {
|
|
2707
|
+
const store = await loadAuthStore();
|
|
2708
|
+
const environments = [];
|
|
2709
|
+
const hasStaging = store.staging.default !== null || Object.keys(store.staging.profiles).length > 0;
|
|
2710
|
+
const hasProduction = store.production.default !== null || Object.keys(store.production.profiles).length > 0;
|
|
2711
|
+
if (hasStaging) environments.push("staging");
|
|
2712
|
+
if (hasProduction) environments.push("production");
|
|
2713
|
+
return environments;
|
|
2643
2714
|
}
|
|
2644
2715
|
|
|
2645
2716
|
// src/lib/auth/http-server/server.ts
|
|
@@ -7260,15 +7331,7 @@ function createDevNamespace(client) {
|
|
|
7260
7331
|
throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
|
|
7261
7332
|
}
|
|
7262
7333
|
}
|
|
7263
|
-
const
|
|
7264
|
-
const anyClient = client;
|
|
7265
|
-
try {
|
|
7266
|
-
return typeof anyClient.getBaseUrl === "function" ? anyClient.getBaseUrl() : "/api";
|
|
7267
|
-
} catch {
|
|
7268
|
-
return "/api";
|
|
7269
|
-
}
|
|
7270
|
-
})();
|
|
7271
|
-
const finalizeUrl = baseUrl.replace(/\/$/, "") + "/games/uploads/finalize/";
|
|
7334
|
+
const finalizeUrl = `${client.baseUrl}/games/uploads/finalize/`;
|
|
7272
7335
|
const authToken = client.getToken();
|
|
7273
7336
|
const tokenType = client.getTokenType();
|
|
7274
7337
|
const headers = {
|
|
@@ -8746,13 +8809,13 @@ var init_achievements = () => {
|
|
|
8746
8809
|
function createTimebackNamespace(client) {
|
|
8747
8810
|
return {
|
|
8748
8811
|
recordProgress: (progressData) => {
|
|
8749
|
-
return client["
|
|
8812
|
+
return client["requestGameBackend"]("/api/integrations/timeback/progress", "POST", { progressData });
|
|
8750
8813
|
},
|
|
8751
8814
|
recordSessionEnd: (sessionData) => {
|
|
8752
|
-
return client["
|
|
8815
|
+
return client["requestGameBackend"]("/api/integrations/timeback/session-end", "POST", { sessionData });
|
|
8753
8816
|
},
|
|
8754
8817
|
awardXP: (xpAmount, metadata) => {
|
|
8755
|
-
return client["
|
|
8818
|
+
return client["requestGameBackend"]("/api/integrations/timeback/award-xp", "POST", { xpAmount, metadata });
|
|
8756
8819
|
},
|
|
8757
8820
|
management: {
|
|
8758
8821
|
setup: (request2) => {
|
|
@@ -9061,7 +9124,7 @@ var init_client = __esm2(() => {
|
|
|
9061
9124
|
authContext;
|
|
9062
9125
|
initPayload;
|
|
9063
9126
|
constructor(config) {
|
|
9064
|
-
this.baseUrl = config?.baseUrl
|
|
9127
|
+
this.baseUrl = config?.baseUrl?.endsWith("/api") ? config.baseUrl : `${config?.baseUrl}/api`;
|
|
9065
9128
|
this.gameUrl = config?.gameUrl;
|
|
9066
9129
|
this.gameId = config?.gameId;
|
|
9067
9130
|
this.config = config || {};
|
|
@@ -9077,8 +9140,9 @@ var init_client = __esm2(() => {
|
|
|
9077
9140
|
return `${effectiveBaseUrl}/api`;
|
|
9078
9141
|
}
|
|
9079
9142
|
getGameBackendUrl() {
|
|
9080
|
-
if (!this.gameUrl)
|
|
9081
|
-
|
|
9143
|
+
if (!this.gameUrl) {
|
|
9144
|
+
throw new PlaycademyError("Game backend URL not configured. gameUrl must be set to use game backend features.");
|
|
9145
|
+
}
|
|
9082
9146
|
const isRelative = this.gameUrl.startsWith("/");
|
|
9083
9147
|
const isBrowser2 = typeof window !== "undefined";
|
|
9084
9148
|
const effectiveGameUrl = isRelative && isBrowser2 ? `${window.location.origin}${this.gameUrl}` : this.gameUrl;
|
|
@@ -9115,17 +9179,29 @@ var init_client = __esm2(() => {
|
|
|
9115
9179
|
listener(payload);
|
|
9116
9180
|
});
|
|
9117
9181
|
}
|
|
9118
|
-
async request(path, method, body, headers
|
|
9182
|
+
async request(path, method, body, headers) {
|
|
9119
9183
|
const effectiveHeaders = {
|
|
9120
9184
|
...headers,
|
|
9121
9185
|
...this.authStrategy.getHeaders()
|
|
9122
9186
|
};
|
|
9123
|
-
const targetBaseUrl = useGameBackend ? this.getGameBackendUrl() : this.baseUrl;
|
|
9124
9187
|
return request({
|
|
9125
9188
|
path,
|
|
9126
9189
|
method,
|
|
9127
9190
|
body,
|
|
9128
|
-
baseUrl:
|
|
9191
|
+
baseUrl: this.baseUrl,
|
|
9192
|
+
extraHeaders: effectiveHeaders
|
|
9193
|
+
});
|
|
9194
|
+
}
|
|
9195
|
+
async requestGameBackend(path, method, body, headers) {
|
|
9196
|
+
const effectiveHeaders = {
|
|
9197
|
+
...headers,
|
|
9198
|
+
...this.authStrategy.getHeaders()
|
|
9199
|
+
};
|
|
9200
|
+
return request({
|
|
9201
|
+
path,
|
|
9202
|
+
method,
|
|
9203
|
+
body,
|
|
9204
|
+
baseUrl: this.getGameBackendUrl(),
|
|
9129
9205
|
extraHeaders: effectiveHeaders
|
|
9130
9206
|
});
|
|
9131
9207
|
}
|
|
@@ -9362,9 +9438,8 @@ var logger = {
|
|
|
9362
9438
|
async function createClient() {
|
|
9363
9439
|
const profile = await getCurrentProfile();
|
|
9364
9440
|
const baseUrl = getBaseUrl();
|
|
9365
|
-
const apiBaseUrl = `${baseUrl}/api`;
|
|
9366
9441
|
const client = new PlaycademyClient({
|
|
9367
|
-
baseUrl
|
|
9442
|
+
baseUrl,
|
|
9368
9443
|
token: profile?.token
|
|
9369
9444
|
});
|
|
9370
9445
|
if (profile?.token && profile?.tokenType) {
|
|
@@ -9374,11 +9449,12 @@ async function createClient() {
|
|
|
9374
9449
|
}
|
|
9375
9450
|
async function requireAuthenticatedClient() {
|
|
9376
9451
|
const profile = await getCurrentProfile();
|
|
9452
|
+
const environment = getEnvironment();
|
|
9377
9453
|
if (!profile) {
|
|
9378
9454
|
logger.newLine();
|
|
9379
9455
|
logger.admonition("warning", "Login Required", [
|
|
9380
|
-
|
|
9381
|
-
"Run `playcademy login` to authenticate."
|
|
9456
|
+
`You are not logged into ${environment}.`,
|
|
9457
|
+
environment === "production" ? `Run \`playcademy login --env ${environment}\` to authenticate.` : "Run `playcademy login` to authenticate."
|
|
9382
9458
|
]);
|
|
9383
9459
|
logger.newLine();
|
|
9384
9460
|
process.exit(1);
|
|
@@ -10238,16 +10314,25 @@ async function validateDeployConfig(config) {
|
|
|
10238
10314
|
// src/lib/deploy/interaction.ts
|
|
10239
10315
|
async function selectEnvironment(options) {
|
|
10240
10316
|
let environment = options.env;
|
|
10241
|
-
|
|
10242
|
-
|
|
10317
|
+
if (environment || process.env.PLAYCADEMY_ENV || process.env.PLAYCADEMY_BASE_URL) {
|
|
10318
|
+
if (environment) {
|
|
10319
|
+
setCliContext({ environment });
|
|
10320
|
+
}
|
|
10321
|
+
return;
|
|
10322
|
+
}
|
|
10323
|
+
const authenticatedEnvs = await getAuthenticatedEnvironments();
|
|
10324
|
+
if (authenticatedEnvs.length === 1) {
|
|
10325
|
+
environment = authenticatedEnvs[0];
|
|
10326
|
+
logger.info(`Deploying to ${environment}`, 1);
|
|
10327
|
+
} else if (authenticatedEnvs.length === 2 && !options.dryRun) {
|
|
10243
10328
|
logger.newLine();
|
|
10244
10329
|
environment = await select({
|
|
10245
10330
|
message: "Select deployment environment:",
|
|
10246
10331
|
choices: [
|
|
10247
|
-
{ value: "
|
|
10248
|
-
{ value: "
|
|
10332
|
+
{ value: "staging", name: "Staging (hub.dev.playcademy.net)" },
|
|
10333
|
+
{ value: "production", name: "Production (hub.playcademy.com)" }
|
|
10249
10334
|
],
|
|
10250
|
-
default: "
|
|
10335
|
+
default: "staging"
|
|
10251
10336
|
});
|
|
10252
10337
|
}
|
|
10253
10338
|
if (environment) {
|
|
@@ -10395,10 +10480,12 @@ function displayCurrentConfiguration(context2) {
|
|
|
10395
10480
|
} else if (config.externalUrl) {
|
|
10396
10481
|
logger.data("URL", config.externalUrl, 1);
|
|
10397
10482
|
}
|
|
10398
|
-
if (
|
|
10483
|
+
if (context2.isGameDeployed) {
|
|
10399
10484
|
logger.data("Status", "Deployed", 1);
|
|
10400
|
-
} else {
|
|
10485
|
+
} else if (existingGame) {
|
|
10401
10486
|
logger.data("Status", "Not deployed", 1);
|
|
10487
|
+
} else {
|
|
10488
|
+
logger.data("Status", "New deployment", 1);
|
|
10402
10489
|
}
|
|
10403
10490
|
logger.newLine();
|
|
10404
10491
|
}
|
|
@@ -10427,14 +10514,15 @@ async function reportNoChanges(context2) {
|
|
|
10427
10514
|
function reportDeploymentSuccess(result, context2) {
|
|
10428
10515
|
const { game, backendDeployment } = result;
|
|
10429
10516
|
const { client, previousBackendHash } = context2;
|
|
10430
|
-
const action = context2.
|
|
10517
|
+
const action = context2.isGameDeployed ? "updated" : "deployed";
|
|
10431
10518
|
logger.newLine();
|
|
10432
10519
|
logger.success(`${game.displayName} ${action} successfully!`);
|
|
10433
10520
|
logger.newLine();
|
|
10434
10521
|
const baseUrl = getWebBaseUrl(client.getBaseUrl());
|
|
10435
10522
|
logger.data("Game URL", underline(blueBright(`${baseUrl}/g/${game.slug}`)), 1);
|
|
10436
10523
|
if (backendDeployment || previousBackendHash) {
|
|
10437
|
-
const
|
|
10524
|
+
const gameDomain = GAME_BACKEND_DOMAINS[getEnvironment()];
|
|
10525
|
+
const backendUrl = `https://${game.slug}.${gameDomain}/api`;
|
|
10438
10526
|
logger.data("Backend API", underline(blueBright(backendUrl)), 1);
|
|
10439
10527
|
}
|
|
10440
10528
|
if (game.version !== "external") {
|
|
@@ -10458,10 +10546,10 @@ import { stat as stat2 } from "node:fs/promises";
|
|
|
10458
10546
|
|
|
10459
10547
|
// ../data/src/domains/game/helpers.ts
|
|
10460
10548
|
var isHostedGame = (game) => {
|
|
10461
|
-
return game.gameType === "hosted" && game.assetBundleBase
|
|
10549
|
+
return game.gameType === "hosted" && !!game.assetBundleBase;
|
|
10462
10550
|
};
|
|
10463
10551
|
var isExternalGame = (game) => {
|
|
10464
|
-
return game.gameType === "external" && game.externalUrl
|
|
10552
|
+
return game.gameType === "external" && !!game.externalUrl;
|
|
10465
10553
|
};
|
|
10466
10554
|
function isGameDeployed(game) {
|
|
10467
10555
|
return isHostedGame(game) || isExternalGame(game);
|
|
@@ -10527,9 +10615,9 @@ async function prepareDeploymentContext(options) {
|
|
|
10527
10615
|
configFileName,
|
|
10528
10616
|
client,
|
|
10529
10617
|
projectPath,
|
|
10530
|
-
existingGame
|
|
10618
|
+
existingGame,
|
|
10531
10619
|
deployedGameInfo: deployedGameInfo ?? void 0,
|
|
10532
|
-
|
|
10620
|
+
isGameDeployed: gameIsDeployed,
|
|
10533
10621
|
buildHash,
|
|
10534
10622
|
buildSize,
|
|
10535
10623
|
previousBuildHash: deployedGameInfo?.buildHash,
|
|
@@ -10579,8 +10667,8 @@ async function analyzeChanges(context2) {
|
|
|
10579
10667
|
};
|
|
10580
10668
|
}
|
|
10581
10669
|
async function calculateDeploymentPlan(context2, changes) {
|
|
10582
|
-
const { config,
|
|
10583
|
-
if (
|
|
10670
|
+
const { config, isGameDeployed: isGameDeployed2, projectPath, deployBackend } = context2;
|
|
10671
|
+
if (!isGameDeployed2) {
|
|
10584
10672
|
const needsBackend3 = hasLocalBackend(projectPath) || !!context2.fullConfig?.integrations;
|
|
10585
10673
|
const shouldDeployBackend2 = deployBackend && needsBackend3;
|
|
10586
10674
|
return {
|
|
@@ -11435,19 +11523,28 @@ initCommand.addCommand(configCommand);
|
|
|
11435
11523
|
|
|
11436
11524
|
// src/commands/login.ts
|
|
11437
11525
|
import { input as input4, password, select as select3 } from "@inquirer/prompts";
|
|
11526
|
+
import { bold as bold4, dim as dim6, whiteBright } from "colorette";
|
|
11438
11527
|
import { Command as Command4 } from "commander";
|
|
11439
11528
|
import open from "open";
|
|
11440
11529
|
init_file_loader();
|
|
11441
|
-
var loginCommand = new Command4("login").description("Authenticate with Playcademy").option("-e, --email <email>", "Email address (for email/password auth)").option("-p, --password <password>", "Password (for email/password auth)").option("--sso", "Use Timeback SSO authentication").action(async (options) => {
|
|
11442
|
-
const { email, password: password2, sso } = options;
|
|
11443
|
-
logger
|
|
11530
|
+
var loginCommand = new Command4("login").description("Authenticate with Playcademy").option("-e, --email <email>", "Email address (for email/password auth)").option("-p, --password <password>", "Password (for email/password auth)").option("--sso", "Use Timeback SSO authentication").option("--env <environment>", "Environment to login to (staging or production)").action(async (options) => {
|
|
11531
|
+
const { email, password: password2, sso, env } = options;
|
|
11532
|
+
const environment = ensureEnvironment(env, logger);
|
|
11444
11533
|
const profileName = getProfileName();
|
|
11445
|
-
|
|
11534
|
+
logger.newLine();
|
|
11535
|
+
const existingProfile = await getProfile(environment, profileName);
|
|
11446
11536
|
if (existingProfile) {
|
|
11447
|
-
const email2 = existingProfile.email;
|
|
11448
|
-
|
|
11449
|
-
|
|
11450
|
-
|
|
11537
|
+
const email2 = existingProfile.email || "unknown user";
|
|
11538
|
+
const otherEnv = environment === "staging" ? "production" : "staging";
|
|
11539
|
+
const otherProfile = await getProfile(otherEnv, profileName);
|
|
11540
|
+
logger.admonition("note", "I know you!", [
|
|
11541
|
+
"You are already logged in",
|
|
11542
|
+
"",
|
|
11543
|
+
dim6("Email: ") + bold4(email2),
|
|
11544
|
+
dim6("Environment: ") + bold4(environment),
|
|
11545
|
+
dim6("Profile: ") + bold4(profileName),
|
|
11546
|
+
...otherProfile ? [dim6("Other Profile: ") + bold4(otherEnv)] : []
|
|
11547
|
+
]);
|
|
11451
11548
|
logger.newLine();
|
|
11452
11549
|
return;
|
|
11453
11550
|
}
|
|
@@ -11462,7 +11559,7 @@ var loginCommand = new Command4("login").description("Authenticate with Playcade
|
|
|
11462
11559
|
});
|
|
11463
11560
|
logger.newLine();
|
|
11464
11561
|
if (authMethod === "sso") {
|
|
11465
|
-
await handleSsoLogin(profileName);
|
|
11562
|
+
await handleSsoLogin(environment, profileName);
|
|
11466
11563
|
return;
|
|
11467
11564
|
}
|
|
11468
11565
|
const finalEmail = email || await promptForEmail();
|
|
@@ -11515,14 +11612,17 @@ var loginCommand = new Command4("login").description("Authenticate with Playcade
|
|
|
11515
11612
|
logger.newLine();
|
|
11516
11613
|
await runStep(
|
|
11517
11614
|
`Saving credentials to profile "${profileName}"`,
|
|
11518
|
-
() => saveProfile(profileName, authProfile),
|
|
11615
|
+
() => saveProfile(environment, profileName, authProfile),
|
|
11519
11616
|
"Credentials saved successfully"
|
|
11520
11617
|
);
|
|
11521
|
-
logger.
|
|
11522
|
-
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11618
|
+
logger.newLine();
|
|
11619
|
+
logger.admonition("tip", "Logged In Successfully!", [
|
|
11620
|
+
`Email: ${authProfile.email}`,
|
|
11621
|
+
`Environment: ${environment}`,
|
|
11622
|
+
...profileName !== "default" ? [`Profile: ${profileName}`] : [],
|
|
11623
|
+
"",
|
|
11624
|
+
...profileName !== "default" ? [`Use \`--profile ${profileName}\` with commands to use this profile`] : []
|
|
11625
|
+
]);
|
|
11526
11626
|
logger.newLine();
|
|
11527
11627
|
} catch (error) {
|
|
11528
11628
|
logger.newLine();
|
|
@@ -11533,7 +11633,7 @@ var loginCommand = new Command4("login").description("Authenticate with Playcade
|
|
|
11533
11633
|
process.exit(1);
|
|
11534
11634
|
}
|
|
11535
11635
|
});
|
|
11536
|
-
async function handleSsoLogin(profileName) {
|
|
11636
|
+
async function handleSsoLogin(environment, profileName) {
|
|
11537
11637
|
try {
|
|
11538
11638
|
const serverPromise = startCallbackServer(SSO_AUTH_TIMEOUT_MS);
|
|
11539
11639
|
const baseUrl = getBaseUrl();
|
|
@@ -11578,21 +11678,20 @@ async function handleSsoLogin(profileName) {
|
|
|
11578
11678
|
email: result.data.email
|
|
11579
11679
|
};
|
|
11580
11680
|
logger.newLine();
|
|
11581
|
-
logger.admonition("warning", "Your API Key", [
|
|
11582
|
-
|
|
11681
|
+
logger.admonition("warning", "Your API Key", [
|
|
11682
|
+
"",
|
|
11683
|
+
whiteBright(bold4(apiKeyResult.apiKey)),
|
|
11684
|
+
"",
|
|
11685
|
+
"Save this key securely - it will not be shown again!"
|
|
11686
|
+
]);
|
|
11583
11687
|
logger.newLine();
|
|
11584
11688
|
await runStep(
|
|
11585
11689
|
`Saving credentials to profile "${profileName}"`,
|
|
11586
|
-
() => saveProfile(profileName, authProfile),
|
|
11587
|
-
"Credentials saved successfully"
|
|
11690
|
+
() => saveProfile(environment, profileName, authProfile),
|
|
11691
|
+
"Credentials saved successfully",
|
|
11692
|
+
{ silent: true }
|
|
11588
11693
|
);
|
|
11589
|
-
logger.success(`Logged in as ${authProfile.email}`);
|
|
11590
|
-
if (profileName !== "default") {
|
|
11591
|
-
logger.newLine();
|
|
11592
|
-
logger.info(`Profile saved as "${profileName}"`);
|
|
11593
|
-
logger.newLine();
|
|
11594
|
-
logger.aside(`Use \`--profile ${profileName}\` with commands to use this profile`, 1);
|
|
11595
|
-
}
|
|
11694
|
+
logger.success(`Logged in to ${environment} as ${authProfile.email}`);
|
|
11596
11695
|
logger.newLine();
|
|
11597
11696
|
} catch (error) {
|
|
11598
11697
|
logger.newLine();
|
|
@@ -11631,27 +11730,23 @@ async function promptForPassword() {
|
|
|
11631
11730
|
|
|
11632
11731
|
// src/commands/logout.ts
|
|
11633
11732
|
import { Command as Command5 } from "commander";
|
|
11634
|
-
var logoutCommand = new Command5("logout").description("Log out from Playcademy").
|
|
11635
|
-
const {
|
|
11733
|
+
var logoutCommand = new Command5("logout").description("Log out from Playcademy").argument("[profile]", "Profile name to logout from", "default").option("--env <environment>", "Environment to logout from (staging or production)").action(async (profileName, options) => {
|
|
11734
|
+
const { env } = options;
|
|
11735
|
+
const environment = ensureEnvironment(env, logger);
|
|
11636
11736
|
try {
|
|
11637
11737
|
logger.newLine();
|
|
11638
|
-
const profile = await getProfile(profileName);
|
|
11738
|
+
const profile = await getProfile(environment, profileName);
|
|
11639
11739
|
if (!profile) {
|
|
11640
|
-
logger.error(`Profile "${profileName}" not found`);
|
|
11740
|
+
logger.error(`Profile "${profileName}" not found in ${environment}`);
|
|
11741
|
+
logger.newLine();
|
|
11641
11742
|
logger.info("Use `playcademy profiles list` to see available profiles");
|
|
11642
11743
|
logger.newLine();
|
|
11643
11744
|
process.exit(1);
|
|
11644
11745
|
}
|
|
11645
|
-
await removeProfile(profileName);
|
|
11646
|
-
logger.success(
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
}
|
|
11650
|
-
if (profileName === "default") {
|
|
11651
|
-
logger.info("To log in again: playcademy login");
|
|
11652
|
-
} else {
|
|
11653
|
-
logger.info(`To log in again: playcademy login --profile ${profileName}`);
|
|
11654
|
-
}
|
|
11746
|
+
await removeProfile(environment, profileName);
|
|
11747
|
+
logger.success(
|
|
11748
|
+
`Logged out from ${environment} profile "${profileName}" (${profile.email})`
|
|
11749
|
+
);
|
|
11655
11750
|
logger.newLine();
|
|
11656
11751
|
} catch (error) {
|
|
11657
11752
|
logger.newLine();
|
|
@@ -11665,18 +11760,24 @@ var logoutCommand = new Command5("logout").description("Log out from Playcademy"
|
|
|
11665
11760
|
|
|
11666
11761
|
// src/commands/me.ts
|
|
11667
11762
|
import { Command as Command6 } from "commander";
|
|
11668
|
-
var meCommand = new Command6("me").description("Display current user information").
|
|
11763
|
+
var meCommand = new Command6("me").description("Display current user information").option(
|
|
11764
|
+
"--env <environment>",
|
|
11765
|
+
"Environment to check user information from (staging or production)"
|
|
11766
|
+
).action(async (options) => {
|
|
11767
|
+
const { env } = options;
|
|
11669
11768
|
try {
|
|
11769
|
+
const environment = ensureEnvironment(env, logger);
|
|
11670
11770
|
const client = await requireAuthenticatedClient();
|
|
11671
11771
|
logger.newLine();
|
|
11672
11772
|
const user = await runStep(
|
|
11673
|
-
|
|
11773
|
+
`Fetching user information from ${environment}`,
|
|
11674
11774
|
client.users.me,
|
|
11675
11775
|
"User information retrieved"
|
|
11676
11776
|
);
|
|
11677
11777
|
logger.newLine();
|
|
11678
11778
|
logger.data("ID", user.id, 1);
|
|
11679
11779
|
logger.data("Email", user.email, 1);
|
|
11780
|
+
logger.data("Environment", environment, 1);
|
|
11680
11781
|
if (user.username) {
|
|
11681
11782
|
logger.data("Username", user.username, 1);
|
|
11682
11783
|
}
|
|
@@ -11724,7 +11825,8 @@ async function executeDeployment(plan, context2) {
|
|
|
11724
11825
|
return { game, backendDeployment };
|
|
11725
11826
|
}
|
|
11726
11827
|
async function runDeployment(options) {
|
|
11727
|
-
|
|
11828
|
+
const environment = ensureEnvironment(options.env, logger);
|
|
11829
|
+
await selectEnvironment({ env: environment, dryRun: options.dryRun });
|
|
11728
11830
|
const context2 = await prepareDeploymentContext(options);
|
|
11729
11831
|
displayCurrentConfiguration(context2);
|
|
11730
11832
|
const didPrompt = await promptForMissingConfig(context2);
|
|
@@ -11789,17 +11891,19 @@ import { Command as Command10 } from "commander";
|
|
|
11789
11891
|
// src/commands/games/delete.ts
|
|
11790
11892
|
import { confirm as confirm5, input as input5, select as select4 } from "@inquirer/prompts";
|
|
11791
11893
|
import { Command as Command8 } from "commander";
|
|
11792
|
-
var deleteCommand = new Command8("delete").alias("rm").alias("remove").description("Delete a game").argument("[slug]", "Game slug to delete").option("-f, --force", "Skip confirmation prompt").action(async (slug, options) => {
|
|
11894
|
+
var deleteCommand = new Command8("delete").alias("rm").alias("remove").description("Delete a game").argument("[slug]", "Game slug to delete").option("-f, --force", "Skip confirmation prompt").option("--env <environment>", "Environment to delete game from (staging or production)").action(async (slug, options) => {
|
|
11895
|
+
const { env } = options;
|
|
11793
11896
|
try {
|
|
11794
|
-
logger
|
|
11897
|
+
const environment = ensureEnvironment(env, logger);
|
|
11795
11898
|
const client = await requireAuthenticatedClient();
|
|
11899
|
+
logger.newLine();
|
|
11796
11900
|
const games2 = await runStep(
|
|
11797
|
-
|
|
11901
|
+
`Fetching games from ${environment}`,
|
|
11798
11902
|
client.games.list,
|
|
11799
|
-
(games3) => `Found ${games3.length} game${games3.length === 1 ? "" : "s"}`
|
|
11903
|
+
(games3) => `Found ${games3.length} game${games3.length === 1 ? "" : "s"} in ${environment}`
|
|
11800
11904
|
);
|
|
11801
11905
|
if (games2.length === 0) {
|
|
11802
|
-
logger.info(
|
|
11906
|
+
logger.info(`No games found in ${environment}`);
|
|
11803
11907
|
return;
|
|
11804
11908
|
}
|
|
11805
11909
|
logger.newLine();
|
|
@@ -11847,11 +11951,11 @@ var deleteCommand = new Command8("delete").alias("rm").alias("remove").descripti
|
|
|
11847
11951
|
logger.newLine();
|
|
11848
11952
|
}
|
|
11849
11953
|
await runStep(
|
|
11850
|
-
|
|
11954
|
+
`Deleting game from ${environment}`,
|
|
11851
11955
|
async () => {
|
|
11852
11956
|
await client.dev.games.delete(game.id);
|
|
11853
11957
|
},
|
|
11854
|
-
|
|
11958
|
+
`Game deleted successfully from ${environment}`
|
|
11855
11959
|
);
|
|
11856
11960
|
const deployedGame = await getDeployedGame(getWorkspace());
|
|
11857
11961
|
if (deployedGame && deployedGame.gameId === game.id) {
|
|
@@ -11870,14 +11974,16 @@ var deleteCommand = new Command8("delete").alias("rm").alias("remove").descripti
|
|
|
11870
11974
|
|
|
11871
11975
|
// src/commands/games/list.ts
|
|
11872
11976
|
import { Command as Command9 } from "commander";
|
|
11873
|
-
var listCommand = new Command9("list").alias("ls").description("List all games").action(async () => {
|
|
11977
|
+
var listCommand = new Command9("list").alias("ls").description("List all games").option("--env <environment>", "Environment to list games from (staging or production)").action(async (options) => {
|
|
11978
|
+
const { env } = options;
|
|
11874
11979
|
try {
|
|
11980
|
+
const environment = ensureEnvironment(env, logger);
|
|
11875
11981
|
const client = await requireAuthenticatedClient();
|
|
11876
11982
|
logger.newLine();
|
|
11877
11983
|
const games2 = await runStep(
|
|
11878
|
-
|
|
11984
|
+
`Fetching games from ${environment}`,
|
|
11879
11985
|
client.games.list,
|
|
11880
|
-
(games3) => `Found ${games3.length} game${games3.length === 1 ? "" : "s"}`
|
|
11986
|
+
(games3) => `Found ${games3.length} game${games3.length === 1 ? "" : "s"} in ${environment}`
|
|
11881
11987
|
);
|
|
11882
11988
|
if (games2.length === 0) {
|
|
11883
11989
|
logger.info("No games found");
|
|
@@ -11916,22 +12022,30 @@ import { Command as Command13 } from "commander";
|
|
|
11916
12022
|
|
|
11917
12023
|
// src/commands/dev/apply.ts
|
|
11918
12024
|
import { Command as Command11 } from "commander";
|
|
11919
|
-
var applyCommand = new Command11("apply").description("Apply for developer status").
|
|
12025
|
+
var applyCommand = new Command11("apply").description("Apply for developer status").option(
|
|
12026
|
+
"--env <environment>",
|
|
12027
|
+
"Environment to apply for developer status in (staging or production)"
|
|
12028
|
+
).action(async (options) => {
|
|
12029
|
+
const { env } = options;
|
|
11920
12030
|
try {
|
|
11921
|
-
logger
|
|
12031
|
+
const environment = ensureEnvironment(env, logger);
|
|
11922
12032
|
const client = await requireAuthenticatedClient();
|
|
12033
|
+
logger.newLine();
|
|
11923
12034
|
const currentStatus = await runStep(
|
|
11924
|
-
|
|
12035
|
+
`Checking current developer status in ${environment}`,
|
|
11925
12036
|
client.dev.status.get,
|
|
11926
|
-
|
|
12037
|
+
`Current status retrieved from ${environment}`
|
|
11927
12038
|
);
|
|
12039
|
+
logger.newLine();
|
|
11928
12040
|
if (currentStatus === "approved") {
|
|
11929
|
-
logger.
|
|
12041
|
+
logger.admonition("note", "Approved!", ["You are already an approved developer!"]);
|
|
11930
12042
|
logger.newLine();
|
|
11931
12043
|
return;
|
|
11932
12044
|
}
|
|
11933
12045
|
if (currentStatus === "pending") {
|
|
11934
|
-
logger.
|
|
12046
|
+
logger.admonition("warning", "Developer Status", [
|
|
12047
|
+
"Your developer application is pending review"
|
|
12048
|
+
]);
|
|
11935
12049
|
logger.newLine();
|
|
11936
12050
|
return;
|
|
11937
12051
|
}
|
|
@@ -11941,14 +12055,15 @@ var applyCommand = new Command11("apply").description("Apply for developer statu
|
|
|
11941
12055
|
"Developer application submitted successfully"
|
|
11942
12056
|
);
|
|
11943
12057
|
logger.newLine();
|
|
11944
|
-
logger.
|
|
11945
|
-
|
|
11946
|
-
|
|
11947
|
-
|
|
11948
|
-
|
|
12058
|
+
logger.admonition("tip", "Application Submitted!", [
|
|
12059
|
+
"Your developer application has been submitted.",
|
|
12060
|
+
"",
|
|
12061
|
+
"What happens next:",
|
|
12062
|
+
" \u2022 We will review your application shortly",
|
|
12063
|
+
" \u2022 Once approved, you can create and deploy games"
|
|
12064
|
+
]);
|
|
11949
12065
|
logger.newLine();
|
|
11950
12066
|
} catch (error) {
|
|
11951
|
-
logger.newLine();
|
|
11952
12067
|
logger.error(
|
|
11953
12068
|
`Failed to apply for developer status: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
11954
12069
|
);
|
|
@@ -11959,31 +12074,37 @@ var applyCommand = new Command11("apply").description("Apply for developer statu
|
|
|
11959
12074
|
|
|
11960
12075
|
// src/commands/dev/get-status.ts
|
|
11961
12076
|
import { Command as Command12 } from "commander";
|
|
11962
|
-
var getStatusCommand = new Command12("status").description("Check your developer status").
|
|
12077
|
+
var getStatusCommand = new Command12("status").description("Check your developer status").option(
|
|
12078
|
+
"--env <environment>",
|
|
12079
|
+
"Environment to check developer status from (staging or production)"
|
|
12080
|
+
).action(async (options) => {
|
|
12081
|
+
const { env } = options;
|
|
11963
12082
|
try {
|
|
11964
|
-
logger
|
|
12083
|
+
const environment = ensureEnvironment(env, logger);
|
|
11965
12084
|
const client = await requireAuthenticatedClient();
|
|
12085
|
+
logger.newLine();
|
|
11966
12086
|
const status = await runStep(
|
|
11967
|
-
|
|
12087
|
+
`Fetching developer status from ${environment}`,
|
|
11968
12088
|
() => client.dev.status.get(),
|
|
11969
|
-
|
|
12089
|
+
`Developer status retrieved from ${environment}`
|
|
11970
12090
|
);
|
|
11971
|
-
logger.newLine();
|
|
11972
|
-
logger.highlight("Developer Status");
|
|
11973
|
-
logger.data("Status", status, 1);
|
|
11974
12091
|
switch (status) {
|
|
11975
12092
|
case "none":
|
|
11976
12093
|
logger.newLine();
|
|
11977
|
-
logger.
|
|
11978
|
-
|
|
12094
|
+
logger.admonition("warning", "Become a Developer", [
|
|
12095
|
+
"Want to create and deploy games?",
|
|
12096
|
+
"Run `playcademy dev apply` to apply for developer status"
|
|
12097
|
+
]);
|
|
11979
12098
|
break;
|
|
11980
12099
|
case "pending":
|
|
11981
12100
|
logger.newLine();
|
|
11982
|
-
logger.
|
|
12101
|
+
logger.admonition("warning", "Developer Status", [
|
|
12102
|
+
"Your application is pending review."
|
|
12103
|
+
]);
|
|
11983
12104
|
break;
|
|
11984
12105
|
case "approved":
|
|
11985
12106
|
logger.newLine();
|
|
11986
|
-
logger.
|
|
12107
|
+
logger.admonition("note", "Approved!", ["You are an approved developer!"]);
|
|
11987
12108
|
break;
|
|
11988
12109
|
default:
|
|
11989
12110
|
logger.newLine();
|
|
@@ -12069,26 +12190,39 @@ import { Command as Command17 } from "commander";
|
|
|
12069
12190
|
import { Command as Command14 } from "commander";
|
|
12070
12191
|
async function listProfilesAction() {
|
|
12071
12192
|
try {
|
|
12072
|
-
const
|
|
12193
|
+
const profilesMap = await listProfiles();
|
|
12073
12194
|
logger.newLine();
|
|
12074
|
-
|
|
12195
|
+
let hasAnyProfiles = false;
|
|
12196
|
+
for (const [, profiles] of profilesMap.entries()) {
|
|
12197
|
+
if (profiles.length > 0) {
|
|
12198
|
+
hasAnyProfiles = true;
|
|
12199
|
+
break;
|
|
12200
|
+
}
|
|
12201
|
+
}
|
|
12202
|
+
if (!hasAnyProfiles) {
|
|
12075
12203
|
logger.warn("No authentication profiles found");
|
|
12076
12204
|
logger.newLine();
|
|
12077
12205
|
logger.admonition("tip", "Getting Started", [
|
|
12078
12206
|
"Run `playcademy login` to create your first profile"
|
|
12079
12207
|
]);
|
|
12208
|
+
logger.newLine();
|
|
12080
12209
|
return;
|
|
12081
12210
|
}
|
|
12082
|
-
const
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
tableData
|
|
12086
|
-
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
|
|
12211
|
+
for (const [environment, profiles] of profilesMap.entries()) {
|
|
12212
|
+
if (profiles.length === 0) continue;
|
|
12213
|
+
logger.highlight(`${environment.charAt(0).toUpperCase() + environment.slice(1)}:`);
|
|
12214
|
+
const tableData = [];
|
|
12215
|
+
for (const profileName of profiles) {
|
|
12216
|
+
const profile = await getProfile(environment, profileName);
|
|
12217
|
+
tableData.push({
|
|
12218
|
+
Profile: profileName,
|
|
12219
|
+
Email: profile?.email ?? ""
|
|
12220
|
+
});
|
|
12221
|
+
}
|
|
12222
|
+
if (tableData.length > 0) {
|
|
12223
|
+
logger.table(tableData);
|
|
12224
|
+
logger.newLine();
|
|
12225
|
+
}
|
|
12092
12226
|
}
|
|
12093
12227
|
} catch (error) {
|
|
12094
12228
|
logger.error(
|
|
@@ -12096,28 +12230,29 @@ async function listProfilesAction() {
|
|
|
12096
12230
|
);
|
|
12097
12231
|
process.exit(1);
|
|
12098
12232
|
}
|
|
12099
|
-
logger.newLine();
|
|
12100
12233
|
}
|
|
12101
12234
|
var listCommand2 = new Command14("list").alias("ls").description("List all stored authentication profiles").action(listProfilesAction);
|
|
12102
12235
|
|
|
12103
12236
|
// src/commands/profiles/remove.ts
|
|
12237
|
+
import { bold as bold5 } from "colorette";
|
|
12104
12238
|
import { Command as Command15 } from "commander";
|
|
12105
|
-
var removeCommand = new Command15("remove
|
|
12239
|
+
var removeCommand = new Command15("remove").alias("rm").description('Remove an authentication profile (defaults to "default")').argument("[name]", "Profile name to remove", "default").option("--env <environment>", "Environment to remove profile from (staging or production)").action(async (name, options) => {
|
|
12240
|
+
const { env } = options;
|
|
12241
|
+
const environment = ensureEnvironment(env, logger);
|
|
12106
12242
|
try {
|
|
12107
12243
|
logger.newLine();
|
|
12108
|
-
const
|
|
12109
|
-
|
|
12110
|
-
|
|
12244
|
+
const profilesMap = await listProfiles();
|
|
12245
|
+
const envProfiles = profilesMap.get(environment) || [];
|
|
12246
|
+
if (!envProfiles.includes(name)) {
|
|
12247
|
+
logger.error(`Profile "${name}" not found in ${environment}`);
|
|
12111
12248
|
logger.newLine();
|
|
12112
12249
|
process.exit(1);
|
|
12113
12250
|
}
|
|
12114
|
-
await removeProfile(name);
|
|
12115
|
-
logger.
|
|
12116
|
-
|
|
12117
|
-
|
|
12118
|
-
|
|
12119
|
-
logger.info("To re-authenticate: playcademy login");
|
|
12120
|
-
}
|
|
12251
|
+
await removeProfile(environment, name);
|
|
12252
|
+
logger.admonition("note", "Removed!", [
|
|
12253
|
+
`Profile ${bold5(name)} removed from ${environment}`,
|
|
12254
|
+
environment === "production" ? `To re-authenticate run \`playcademy login --env ${environment}\`` : "To re-authenticate run `playcademy login`"
|
|
12255
|
+
]);
|
|
12121
12256
|
logger.newLine();
|
|
12122
12257
|
} catch (error) {
|
|
12123
12258
|
logger.newLine();
|
|
@@ -12132,10 +12267,16 @@ var removeCommand = new Command15("remove <name>").alias("rm").description("Remo
|
|
|
12132
12267
|
// src/commands/profiles/reset.ts
|
|
12133
12268
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
12134
12269
|
import { Command as Command16 } from "commander";
|
|
12135
|
-
var resetCommand = new Command16("reset").description(
|
|
12270
|
+
var resetCommand = new Command16("reset").description(
|
|
12271
|
+
"Remove all authentication profiles across all environments (requires confirmation)"
|
|
12272
|
+
).alias("clear").action(async () => {
|
|
12136
12273
|
try {
|
|
12137
|
-
const
|
|
12138
|
-
|
|
12274
|
+
const profilesMap = await listProfiles();
|
|
12275
|
+
let totalProfiles = 0;
|
|
12276
|
+
for (const profiles of profilesMap.values()) {
|
|
12277
|
+
totalProfiles += profiles.length;
|
|
12278
|
+
}
|
|
12279
|
+
if (totalProfiles === 0) {
|
|
12139
12280
|
logger.newLine();
|
|
12140
12281
|
logger.warn("No authentication profiles found");
|
|
12141
12282
|
logger.newLine();
|
|
@@ -12143,14 +12284,21 @@ var resetCommand = new Command16("reset").description("Remove all authentication
|
|
|
12143
12284
|
}
|
|
12144
12285
|
logger.newLine();
|
|
12145
12286
|
logger.warn(
|
|
12146
|
-
`This will remove ${
|
|
12287
|
+
`This will remove ${totalProfiles} authentication profile${totalProfiles > 1 ? "s" : ""} across all environments:`
|
|
12147
12288
|
);
|
|
12148
12289
|
logger.newLine();
|
|
12149
|
-
for (const
|
|
12150
|
-
|
|
12151
|
-
logger.
|
|
12290
|
+
for (const [environment, profiles] of profilesMap.entries()) {
|
|
12291
|
+
if (profiles.length === 0) continue;
|
|
12292
|
+
logger.highlight(
|
|
12293
|
+
`${environment.charAt(0).toUpperCase() + environment.slice(1)}:`,
|
|
12294
|
+
1
|
|
12295
|
+
);
|
|
12296
|
+
for (const profileName of profiles) {
|
|
12297
|
+
const profile = await getProfile(environment, profileName);
|
|
12298
|
+
logger.data(profileName, profile?.email || "", 2);
|
|
12299
|
+
}
|
|
12300
|
+
logger.newLine();
|
|
12152
12301
|
}
|
|
12153
|
-
logger.newLine();
|
|
12154
12302
|
const confirmed = await confirm6({
|
|
12155
12303
|
message: "Are you sure you want to remove all profiles?",
|
|
12156
12304
|
default: false
|
|
@@ -12160,23 +12308,27 @@ var resetCommand = new Command16("reset").description("Remove all authentication
|
|
|
12160
12308
|
return;
|
|
12161
12309
|
}
|
|
12162
12310
|
let removedCount = 0;
|
|
12163
|
-
for (const
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12311
|
+
for (const [environment, profiles] of profilesMap.entries()) {
|
|
12312
|
+
for (const profileName of profiles) {
|
|
12313
|
+
try {
|
|
12314
|
+
await removeProfile(environment, profileName);
|
|
12315
|
+
removedCount++;
|
|
12316
|
+
} catch (error) {
|
|
12317
|
+
logger.newLine();
|
|
12318
|
+
logger.error(
|
|
12319
|
+
`Failed to remove profile "${profileName}" from ${environment}`
|
|
12320
|
+
);
|
|
12321
|
+
logger.error(error instanceof Error ? error.message : "Unknown error");
|
|
12322
|
+
logger.newLine();
|
|
12323
|
+
}
|
|
12172
12324
|
}
|
|
12173
12325
|
}
|
|
12174
|
-
if (removedCount ===
|
|
12326
|
+
if (removedCount === totalProfiles) {
|
|
12175
12327
|
logger.success(
|
|
12176
12328
|
`${removedCount > 1 ? "All " : ""}${removedCount} profile${removedCount > 1 ? "s" : ""} removed successfully`
|
|
12177
12329
|
);
|
|
12178
12330
|
} else {
|
|
12179
|
-
logger.warn(`Removed ${removedCount} of ${
|
|
12331
|
+
logger.warn(`Removed ${removedCount} of ${totalProfiles} profiles`);
|
|
12180
12332
|
}
|
|
12181
12333
|
logger.newLine();
|
|
12182
12334
|
} catch (error) {
|
|
@@ -12201,13 +12353,18 @@ import { Command as Command22 } from "commander";
|
|
|
12201
12353
|
// src/commands/timeback/cleanup.ts
|
|
12202
12354
|
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
12203
12355
|
import { Command as Command18 } from "commander";
|
|
12204
|
-
var cleanupCommand = new Command18("cleanup").description("Remove TimeBack integration for your game").
|
|
12356
|
+
var cleanupCommand = new Command18("cleanup").description("Remove TimeBack integration for your game").option(
|
|
12357
|
+
"--env <environment>",
|
|
12358
|
+
"Environment to remove TimeBack integration from (staging or production)"
|
|
12359
|
+
).action(async (options) => {
|
|
12360
|
+
const { env } = options;
|
|
12205
12361
|
try {
|
|
12206
|
-
logger
|
|
12362
|
+
const environment = ensureEnvironment(env, logger);
|
|
12207
12363
|
const client = await requireAuthenticatedClient();
|
|
12364
|
+
logger.newLine();
|
|
12208
12365
|
const { game } = await getGameFromConfig(client);
|
|
12209
12366
|
const integration = await runStep(
|
|
12210
|
-
|
|
12367
|
+
`Checking for TimeBack integration in ${environment}`,
|
|
12211
12368
|
() => client.timeback.management.get(game.id),
|
|
12212
12369
|
"Integration status checked"
|
|
12213
12370
|
);
|
|
@@ -12230,9 +12387,9 @@ var cleanupCommand = new Command18("cleanup").description("Remove TimeBack integ
|
|
|
12230
12387
|
}
|
|
12231
12388
|
logger.newLine();
|
|
12232
12389
|
await runStep(
|
|
12233
|
-
|
|
12390
|
+
`Removing TimeBack integration from ${environment}`,
|
|
12234
12391
|
() => client.timeback.management.cleanup(game.id),
|
|
12235
|
-
|
|
12392
|
+
`TimeBack integration removed from ${environment}`
|
|
12236
12393
|
);
|
|
12237
12394
|
logger.newLine();
|
|
12238
12395
|
logger.admonition("note", "Note", [
|
|
@@ -12251,8 +12408,13 @@ var cleanupCommand = new Command18("cleanup").description("Remove TimeBack integ
|
|
|
12251
12408
|
// src/commands/timeback/setup.ts
|
|
12252
12409
|
import { Command as Command19 } from "commander";
|
|
12253
12410
|
import dedent from "dedent";
|
|
12254
|
-
var setupCommand = new Command19("setup").description("Set up TimeBack integration for your game").option("--dry-run", "Validate configuration without creating resources").option("--verbose, -v", "Output detailed information").
|
|
12411
|
+
var setupCommand = new Command19("setup").description("Set up TimeBack integration for your game").option("--dry-run", "Validate configuration without creating resources").option("--verbose, -v", "Output detailed information").option(
|
|
12412
|
+
"--env <environment>",
|
|
12413
|
+
"Environment to set up TimeBack integration in (staging or production)"
|
|
12414
|
+
).action(async (options) => {
|
|
12415
|
+
const { env } = options;
|
|
12255
12416
|
try {
|
|
12417
|
+
ensureEnvironment(env, logger);
|
|
12256
12418
|
logger.newLine();
|
|
12257
12419
|
const config = await runStep(
|
|
12258
12420
|
"Loading configuration",
|
|
@@ -12354,8 +12516,13 @@ var setupCommand = new Command19("setup").description("Set up TimeBack integrati
|
|
|
12354
12516
|
import { confirm as confirm8 } from "@inquirer/prompts";
|
|
12355
12517
|
import { green as green3, red as red3 } from "colorette";
|
|
12356
12518
|
import { Command as Command20 } from "commander";
|
|
12357
|
-
var updateCommand = new Command20("update").description("Update TimeBack integration configuration for your game").option("--verbose, -v", "Output detailed information").
|
|
12519
|
+
var updateCommand = new Command20("update").description("Update TimeBack integration configuration for your game").option("--verbose, -v", "Output detailed information").option(
|
|
12520
|
+
"--env <environment>",
|
|
12521
|
+
"Environment to update TimeBack integration in (staging or production)"
|
|
12522
|
+
).action(async (options) => {
|
|
12523
|
+
const { env } = options;
|
|
12358
12524
|
try {
|
|
12525
|
+
ensureEnvironment(env, logger);
|
|
12359
12526
|
logger.newLine();
|
|
12360
12527
|
const config = await runStep(
|
|
12361
12528
|
"Loading configuration",
|
|
@@ -12461,13 +12628,18 @@ var updateCommand = new Command20("update").description("Update TimeBack integra
|
|
|
12461
12628
|
|
|
12462
12629
|
// src/commands/timeback/verify.ts
|
|
12463
12630
|
import { Command as Command21 } from "commander";
|
|
12464
|
-
var verifyCommand = new Command21("verify").description("Verify TimeBack integration for your game").option("--verbose, -v", "Output detailed resource information").
|
|
12631
|
+
var verifyCommand = new Command21("verify").description("Verify TimeBack integration for your game").option("--verbose, -v", "Output detailed resource information").option(
|
|
12632
|
+
"--env <environment>",
|
|
12633
|
+
"Environment to verify TimeBack integration in (staging or production)"
|
|
12634
|
+
).action(async (options) => {
|
|
12635
|
+
const { env } = options;
|
|
12465
12636
|
try {
|
|
12466
|
-
logger
|
|
12637
|
+
const environment = ensureEnvironment(env, logger);
|
|
12467
12638
|
const client = await requireAuthenticatedClient();
|
|
12639
|
+
logger.newLine();
|
|
12468
12640
|
const { game } = await getGameFromConfig(client);
|
|
12469
12641
|
const result = await runStep(
|
|
12470
|
-
|
|
12642
|
+
`Verifying TimeBack integration in ${environment}`,
|
|
12471
12643
|
() => client.timeback.management.verify(game.id),
|
|
12472
12644
|
"Verification complete"
|
|
12473
12645
|
);
|
|
@@ -12508,11 +12680,12 @@ timebackCommand.addCommand(cleanupCommand);
|
|
|
12508
12680
|
var __dirname = dirname4(fileURLToPath2(import.meta.url));
|
|
12509
12681
|
var packageJson = await loadPackageJson({ cwd: __dirname, searchUp: true, required: true });
|
|
12510
12682
|
program.name("playcademy").description("CLI for deploying and managing Playcademy games").version(packageJson?.version || "0.0.0").option("--profile <name>", "Use a specific authentication profile", "default").hook("preAction", (thisCommand) => {
|
|
12511
|
-
const options = thisCommand.
|
|
12683
|
+
const options = thisCommand.optsWithGlobals();
|
|
12512
12684
|
setCliContext({ profile: options.profile, workspace: process.cwd() });
|
|
12513
12685
|
});
|
|
12514
12686
|
program.addCommand(initCommand);
|
|
12515
12687
|
program.addCommand(loginCommand);
|
|
12688
|
+
program.addCommand(logoutCommand);
|
|
12516
12689
|
program.addCommand(profilesCommand);
|
|
12517
12690
|
program.addCommand(meCommand);
|
|
12518
12691
|
program.addCommand(devCommand);
|
|
@@ -12550,6 +12723,7 @@ export {
|
|
|
12550
12723
|
displayRegisteredRoutes,
|
|
12551
12724
|
displayResourcesStatus,
|
|
12552
12725
|
displaySuccessMessage,
|
|
12726
|
+
ensureEnvironment,
|
|
12553
12727
|
ensureGameExists,
|
|
12554
12728
|
findConfigPath,
|
|
12555
12729
|
generateEntryCode,
|
|
@@ -12557,6 +12731,7 @@ export {
|
|
|
12557
12731
|
generateJsonConfig,
|
|
12558
12732
|
getApiUrl,
|
|
12559
12733
|
getAuthPath,
|
|
12734
|
+
getAuthenticatedEnvironments,
|
|
12560
12735
|
getBackendHash,
|
|
12561
12736
|
getBackendSize,
|
|
12562
12737
|
getBaseUrl,
|
|
@@ -12588,6 +12763,7 @@ export {
|
|
|
12588
12763
|
loadGameStore,
|
|
12589
12764
|
logger,
|
|
12590
12765
|
needsBackend,
|
|
12766
|
+
normalizeEnvironment,
|
|
12591
12767
|
prepareDeploymentContext,
|
|
12592
12768
|
processConfigVariables,
|
|
12593
12769
|
promptForApiRoutes,
|
package/dist/types.d.ts
CHANGED
|
@@ -23,15 +23,28 @@ interface AuthProfile {
|
|
|
23
23
|
/** ISO 8601 date string - when the token expires (if applicable) */
|
|
24
24
|
expiresAt?: string;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Environment-specific auth profiles
|
|
28
|
+
* Each environment (staging, production) has its own set of profiles
|
|
29
|
+
*/
|
|
30
|
+
interface EnvironmentAuthProfiles {
|
|
31
|
+
/** Default authentication profile for this environment */
|
|
32
|
+
default: AuthProfile | null;
|
|
33
|
+
/** Named authentication profiles for this environment */
|
|
34
|
+
profiles: Record<string, AuthProfile>;
|
|
35
|
+
}
|
|
26
36
|
/**
|
|
27
37
|
* The complete auth store structure
|
|
28
38
|
* CLI-specific type for ~/.playcademy/auth.json
|
|
39
|
+
*
|
|
40
|
+
* Profiles are scoped per environment to ensure staging and production
|
|
41
|
+
* credentials are kept separate for security.
|
|
29
42
|
*/
|
|
30
43
|
interface AuthStore {
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
|
|
44
|
+
/** Staging environment profiles */
|
|
45
|
+
staging: EnvironmentAuthProfiles;
|
|
46
|
+
/** Production environment profiles */
|
|
47
|
+
production: EnvironmentAuthProfiles;
|
|
35
48
|
}
|
|
36
49
|
/**
|
|
37
50
|
* Login credentials for email/password authentication
|
|
@@ -486,7 +499,7 @@ interface DeploymentContext {
|
|
|
486
499
|
projectPath: string;
|
|
487
500
|
existingGame?: Game;
|
|
488
501
|
deployedGameInfo?: DeployedGameInfo;
|
|
489
|
-
|
|
502
|
+
isGameDeployed: boolean;
|
|
490
503
|
buildHash?: string;
|
|
491
504
|
buildSize?: number;
|
|
492
505
|
previousBuildHash?: string;
|
|
@@ -625,4 +638,4 @@ interface PreviewResponse {
|
|
|
625
638
|
qrCode?: string;
|
|
626
639
|
}
|
|
627
640
|
|
|
628
|
-
export type { ApiConfig, ApiErrorResponse, ApiKeyListItem, ApiKeyWithSecret, ApiRequestOptions, AuthProfile, AuthStore, BackendDeploymentWithHash, CallbackServerResult, CreateApiKeyResponse, DeployConfig, DeployNewGameOptions, DeployedGameInfo, DeploymentChanges, DeploymentContext, DeploymentPlan, DeploymentResult, GameStore, IntegrationsConfig, LoginCredentials, LoginResponse, PlaycademyConfig, PreviewOptions, PreviewResponse, SignInResponse, SsoCallbackData, TimebackIntegrationConfig, TokenType, UpdateExistingGameOptions };
|
|
641
|
+
export type { ApiConfig, ApiErrorResponse, ApiKeyListItem, ApiKeyWithSecret, ApiRequestOptions, AuthProfile, AuthStore, BackendDeploymentWithHash, CallbackServerResult, CreateApiKeyResponse, DeployConfig, DeployNewGameOptions, DeployedGameInfo, DeploymentChanges, DeploymentContext, DeploymentPlan, DeploymentResult, EnvironmentAuthProfiles, GameStore, IntegrationsConfig, LoginCredentials, LoginResponse, PlaycademyConfig, PreviewOptions, PreviewResponse, SignInResponse, SsoCallbackData, TimebackIntegrationConfig, TokenType, UpdateExistingGameOptions };
|
package/dist/utils.js
CHANGED
|
@@ -4548,15 +4548,7 @@ function createDevNamespace(client) {
|
|
|
4548
4548
|
throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
|
|
4549
4549
|
}
|
|
4550
4550
|
}
|
|
4551
|
-
const
|
|
4552
|
-
const anyClient = client;
|
|
4553
|
-
try {
|
|
4554
|
-
return typeof anyClient.getBaseUrl === "function" ? anyClient.getBaseUrl() : "/api";
|
|
4555
|
-
} catch {
|
|
4556
|
-
return "/api";
|
|
4557
|
-
}
|
|
4558
|
-
})();
|
|
4559
|
-
const finalizeUrl = baseUrl.replace(/\/$/, "") + "/games/uploads/finalize/";
|
|
4551
|
+
const finalizeUrl = `${client.baseUrl}/games/uploads/finalize/`;
|
|
4560
4552
|
const authToken = client.getToken();
|
|
4561
4553
|
const tokenType = client.getTokenType();
|
|
4562
4554
|
const headers = {
|
|
@@ -6034,13 +6026,13 @@ var init_achievements = () => {
|
|
|
6034
6026
|
function createTimebackNamespace(client) {
|
|
6035
6027
|
return {
|
|
6036
6028
|
recordProgress: (progressData) => {
|
|
6037
|
-
return client["
|
|
6029
|
+
return client["requestGameBackend"]("/api/integrations/timeback/progress", "POST", { progressData });
|
|
6038
6030
|
},
|
|
6039
6031
|
recordSessionEnd: (sessionData) => {
|
|
6040
|
-
return client["
|
|
6032
|
+
return client["requestGameBackend"]("/api/integrations/timeback/session-end", "POST", { sessionData });
|
|
6041
6033
|
},
|
|
6042
6034
|
awardXP: (xpAmount, metadata) => {
|
|
6043
|
-
return client["
|
|
6035
|
+
return client["requestGameBackend"]("/api/integrations/timeback/award-xp", "POST", { xpAmount, metadata });
|
|
6044
6036
|
},
|
|
6045
6037
|
management: {
|
|
6046
6038
|
setup: (request2) => {
|
|
@@ -6349,7 +6341,7 @@ var init_client = __esm2(() => {
|
|
|
6349
6341
|
authContext;
|
|
6350
6342
|
initPayload;
|
|
6351
6343
|
constructor(config) {
|
|
6352
|
-
this.baseUrl = config?.baseUrl
|
|
6344
|
+
this.baseUrl = config?.baseUrl?.endsWith("/api") ? config.baseUrl : `${config?.baseUrl}/api`;
|
|
6353
6345
|
this.gameUrl = config?.gameUrl;
|
|
6354
6346
|
this.gameId = config?.gameId;
|
|
6355
6347
|
this.config = config || {};
|
|
@@ -6365,8 +6357,9 @@ var init_client = __esm2(() => {
|
|
|
6365
6357
|
return `${effectiveBaseUrl}/api`;
|
|
6366
6358
|
}
|
|
6367
6359
|
getGameBackendUrl() {
|
|
6368
|
-
if (!this.gameUrl)
|
|
6369
|
-
|
|
6360
|
+
if (!this.gameUrl) {
|
|
6361
|
+
throw new PlaycademyError("Game backend URL not configured. gameUrl must be set to use game backend features.");
|
|
6362
|
+
}
|
|
6370
6363
|
const isRelative = this.gameUrl.startsWith("/");
|
|
6371
6364
|
const isBrowser2 = typeof window !== "undefined";
|
|
6372
6365
|
const effectiveGameUrl = isRelative && isBrowser2 ? `${window.location.origin}${this.gameUrl}` : this.gameUrl;
|
|
@@ -6403,17 +6396,29 @@ var init_client = __esm2(() => {
|
|
|
6403
6396
|
listener(payload);
|
|
6404
6397
|
});
|
|
6405
6398
|
}
|
|
6406
|
-
async request(path, method, body, headers
|
|
6399
|
+
async request(path, method, body, headers) {
|
|
6400
|
+
const effectiveHeaders = {
|
|
6401
|
+
...headers,
|
|
6402
|
+
...this.authStrategy.getHeaders()
|
|
6403
|
+
};
|
|
6404
|
+
return request({
|
|
6405
|
+
path,
|
|
6406
|
+
method,
|
|
6407
|
+
body,
|
|
6408
|
+
baseUrl: this.baseUrl,
|
|
6409
|
+
extraHeaders: effectiveHeaders
|
|
6410
|
+
});
|
|
6411
|
+
}
|
|
6412
|
+
async requestGameBackend(path, method, body, headers) {
|
|
6407
6413
|
const effectiveHeaders = {
|
|
6408
6414
|
...headers,
|
|
6409
6415
|
...this.authStrategy.getHeaders()
|
|
6410
6416
|
};
|
|
6411
|
-
const targetBaseUrl = useGameBackend ? this.getGameBackendUrl() : this.baseUrl;
|
|
6412
6417
|
return request({
|
|
6413
6418
|
path,
|
|
6414
6419
|
method,
|
|
6415
6420
|
body,
|
|
6416
|
-
baseUrl:
|
|
6421
|
+
baseUrl: this.getGameBackendUrl(),
|
|
6417
6422
|
extraHeaders: effectiveHeaders
|
|
6418
6423
|
});
|
|
6419
6424
|
}
|