playcademy 0.14.33 → 0.15.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 CHANGED
@@ -712,6 +712,7 @@ var GAME_WORKER_DOMAINS = {
712
712
  production: "playcademy.gg",
713
713
  staging: "staging.playcademy.gg"
714
714
  };
715
+ var LOG_COLLECTOR_URL = "https://logs.playcademy.gg";
715
716
 
716
717
  // ../constants/src/overworld.ts
717
718
  var ITEM_SLUGS = {
@@ -3959,7 +3960,7 @@ import { join as join12 } from "path";
3959
3960
  // package.json with { type: 'json' }
3960
3961
  var package_default2 = {
3961
3962
  name: "playcademy",
3962
- version: "0.14.32",
3963
+ version: "0.14.34",
3963
3964
  type: "module",
3964
3965
  exports: {
3965
3966
  ".": {
@@ -4022,7 +4023,8 @@ var package_default2 = {
4022
4023
  "json-colorizer": "^3.0.1",
4023
4024
  jszip: "^3.10.1",
4024
4025
  miniflare: "^4.20251008.0",
4025
- open: "^10.2.0"
4026
+ open: "^10.2.0",
4027
+ ws: "^8.18.3"
4026
4028
  },
4027
4029
  devDependencies: {
4028
4030
  "@cloudflare/workers-types": "^4.20251011.0",
@@ -4032,6 +4034,7 @@ var package_default2 = {
4032
4034
  "@playcademy/timeback": "workspace:*",
4033
4035
  "@playcademy/utils": "workspace:*",
4034
4036
  "@types/bun": "latest",
4037
+ "@types/ws": "^8.18.1",
4035
4038
  bumpp: "^10.2.3",
4036
4039
  rollup: "^4.50.2",
4037
4040
  "rollup-plugin-dts": "^6.2.3"
@@ -5874,9 +5877,6 @@ async function saveDeploymentState(game, backendMetadata, context2) {
5874
5877
  if (backendMetadata.schemaSnapshot !== void 0) {
5875
5878
  deploymentState.schemaSnapshot = backendMetadata.schemaSnapshot;
5876
5879
  }
5877
- if (backendMetadata.deployedSecrets !== void 0) {
5878
- deploymentState.deployedSecrets = backendMetadata.deployedSecrets;
5879
- }
5880
5880
  } else if (context2.deployedGameInfo) {
5881
5881
  deploymentState.backendBundleHash = context2.deployedGameInfo.backendBundleHash;
5882
5882
  deploymentState.backendSize = context2.deployedGameInfo.backendSize;
@@ -5888,12 +5888,6 @@ async function saveDeploymentState(game, backendMetadata, context2) {
5888
5888
  if (context2.deployedGameInfo.schemaSnapshot !== void 0) {
5889
5889
  deploymentState.schemaSnapshot = context2.deployedGameInfo.schemaSnapshot;
5890
5890
  }
5891
- if (context2.deployedGameInfo.deployedSecrets !== void 0) {
5892
- deploymentState.deployedSecrets = context2.deployedGameInfo.deployedSecrets;
5893
- }
5894
- }
5895
- if (context2.currentSecretKeys !== void 0) {
5896
- deploymentState.deployedSecrets = context2.currentSecretKeys;
5897
5891
  }
5898
5892
  await saveDeployedGame(projectPath, deploymentState);
5899
5893
  }
@@ -7089,25 +7083,6 @@ function displayDeploymentDiff(options) {
7089
7083
  logger.newLine();
7090
7084
  }
7091
7085
  }
7092
- const previousSecretKeys = options.secrets?.previousKeys;
7093
- const currentSecretKeys = options.secrets?.currentKeys;
7094
- if (previousSecretKeys !== void 0 && currentSecretKeys !== void 0) {
7095
- const added = currentSecretKeys.filter((k) => !previousSecretKeys.includes(k));
7096
- const removed = previousSecretKeys.filter((k) => !currentSecretKeys.includes(k));
7097
- const hasSecretsChanges = added.length > 0 || removed.length > 0;
7098
- if (hasSecretsChanges) {
7099
- logger.bold("Secrets", 1);
7100
- if (added.length > 0) {
7101
- const addedList = added.map((k) => green5(k)).join(", ");
7102
- logger.data("Added", addedList, 2);
7103
- }
7104
- if (removed.length > 0) {
7105
- const removedList = removed.map((k) => red3(k)).join(", ");
7106
- logger.data("Removed", removedList, 2);
7107
- }
7108
- logger.newLine();
7109
- }
7110
- }
7111
7086
  }
7112
7087
 
7113
7088
  // src/lib/deploy/interaction.ts
@@ -7382,10 +7357,6 @@ async function confirmDeploymentPlan(plan, context2) {
7382
7357
  previousKeys: context2.previousIntegrationKeys,
7383
7358
  currentKeys: currentIntegrationKeys,
7384
7359
  metadata: plan.changes.integrationsMetadata
7385
- },
7386
- secrets: {
7387
- previousKeys: context2.deployedGameInfo?.deployedSecrets,
7388
- currentKeys: context2.currentSecretKeys
7389
7360
  }
7390
7361
  });
7391
7362
  if (isNonInteractive()) {
@@ -7550,10 +7521,6 @@ async function reportDryRun(plan, context2) {
7550
7521
  previousKeys: context2.previousIntegrationKeys,
7551
7522
  currentKeys: currentIntegrationKeys,
7552
7523
  metadata: plan.changes.integrationsMetadata
7553
- },
7554
- secrets: {
7555
- previousKeys: context2.deployedGameInfo?.deployedSecrets,
7556
- currentKeys: context2.currentSecretKeys
7557
7524
  }
7558
7525
  });
7559
7526
  } else {
@@ -8013,45 +7980,6 @@ var INTERACTION_TYPE = Object.fromEntries(
8013
7980
  interactionTypeEnum.enumValues.map((value) => [value, value])
8014
7981
  );
8015
7982
 
8016
- // src/lib/deploy/secrets.ts
8017
- import { ApiError as ApiError3 } from "@playcademy/sdk/internal";
8018
- function compareSecrets(current, previous) {
8019
- const currentKeys = Object.keys(current);
8020
- const changes = [];
8021
- for (const key of currentKeys) {
8022
- if (!previous.includes(key)) {
8023
- changes.push({ key, type: "added" });
8024
- }
8025
- }
8026
- for (const key of previous) {
8027
- if (!currentKeys.includes(key)) {
8028
- changes.push({ key, type: "removed" });
8029
- }
8030
- }
8031
- return {
8032
- hasChanges: changes.length > 0,
8033
- changes
8034
- };
8035
- }
8036
- async function fetchSecretsForDeployment(slug) {
8037
- try {
8038
- const client = await requireAuthenticatedClient();
8039
- const secrets = await client.dev.games.secrets.get(slug);
8040
- const keys = Object.keys(secrets);
8041
- return { secrets, keys };
8042
- } catch (error) {
8043
- if (error instanceof ApiError3 && error.status === 404) {
8044
- logger.debug(`[Secrets] No secrets configured for project (404)`);
8045
- return { secrets: {}, keys: [] };
8046
- }
8047
- logger.warn(
8048
- `Could not fetch secrets: ${error instanceof Error ? error.message : String(error)}`
8049
- );
8050
- logger.debug(`[Secrets] Error details: ${error}`);
8051
- return { secrets: {}, keys: [] };
8052
- }
8053
- }
8054
-
8055
7983
  // src/lib/deploy/preparation.ts
8056
7984
  async function updateBuildMetadata(context2) {
8057
7985
  const { config, verbose } = context2;
@@ -8125,11 +8053,6 @@ async function prepareDeploymentContext(options) {
8125
8053
  buildHash = await hashFile(finalConfig.buildPath);
8126
8054
  }
8127
8055
  }
8128
- let currentSecretKeys;
8129
- if (finalConfig.slug && gameIsDeployed) {
8130
- const { keys } = await fetchSecretsForDeployment(finalConfig.slug);
8131
- currentSecretKeys = keys;
8132
- }
8133
8056
  return {
8134
8057
  config: finalConfig,
8135
8058
  fullConfig,
@@ -8151,7 +8074,6 @@ async function prepareDeploymentContext(options) {
8151
8074
  previousResources: deployedGameInfo?.deployedResources,
8152
8075
  previousIntegrationKeys: deployedGameInfo?.integrationKeys,
8153
8076
  previousIntegrationsHash: deployedGameInfo?.integrationsHash,
8154
- currentSecretKeys,
8155
8077
  isDryRun: options.dryRun ?? false,
8156
8078
  deployBackend: options.backend !== false,
8157
8079
  forceBackend: options.forceBackend ?? false,
@@ -8204,7 +8126,6 @@ async function analyzeChanges(context2) {
8204
8126
  }
8205
8127
  let backendChanged;
8206
8128
  let schemaChanged = false;
8207
- let secretsChanged = false;
8208
8129
  if (!deployBackend) {
8209
8130
  backendChanged = void 0;
8210
8131
  } else if (!context2.fullConfig) {
@@ -8234,19 +8155,6 @@ async function analyzeChanges(context2) {
8234
8155
  }
8235
8156
  }
8236
8157
  schemaChanged = await hasSchemaChanged(context2.deployedGameInfo?.schemaSnapshot);
8237
- if (context2.currentSecretKeys) {
8238
- const currentSecretKeys = context2.currentSecretKeys;
8239
- const previousSecretKeys = context2.deployedGameInfo?.deployedSecrets || [];
8240
- const secretsComparison = compareSecrets(
8241
- Object.fromEntries(currentSecretKeys.map((k) => [k, ""])),
8242
- // We only care about keys
8243
- previousSecretKeys
8244
- );
8245
- secretsChanged = secretsComparison.hasChanges;
8246
- if (secretsChanged && backendChanged !== void 0) {
8247
- backendChanged = backendChanged || secretsChanged;
8248
- }
8249
- }
8250
8158
  }
8251
8159
  let integrationsMetadataChanged;
8252
8160
  if (!context2.fullConfig?.integrations) {
@@ -8271,8 +8179,7 @@ async function analyzeChanges(context2) {
8271
8179
  backend: backendChanged,
8272
8180
  integrations: integrationsMetadataChanged,
8273
8181
  integrationsMetadata,
8274
- schema: schemaChanged,
8275
- secrets: secretsChanged
8182
+ schema: schemaChanged
8276
8183
  };
8277
8184
  }
8278
8185
  async function calculateDeploymentPlan(context2, changes) {
@@ -8304,8 +8211,7 @@ async function calculateDeploymentPlan(context2, changes) {
8304
8211
  }
8305
8212
  const shouldUpdateGame = changes.build !== false || changes.config;
8306
8213
  const shouldUploadBuild = changes.build === true;
8307
- const hasSecretsForUpdate = (context2.currentSecretKeys?.length ?? 0) > 0;
8308
- const needsBackend2 = hasLocalCustomRoutes(projectPath, context2.fullConfig) || !!context2.fullConfig?.integrations || hasSecretsForUpdate;
8214
+ const needsBackend2 = hasLocalCustomRoutes(projectPath, context2.fullConfig) || !!context2.fullConfig?.integrations;
8309
8215
  const shouldDeployBackend = deployBackend && (backendHasChanges || forceBackend || needsBackend2);
8310
8216
  return {
8311
8217
  action: "update-existing",
@@ -8539,20 +8445,11 @@ async function deployGame(context2, shouldUploadBuild, shouldDeployBackend) {
8539
8445
  bindings.bucket = [deploymentId];
8540
8446
  }
8541
8447
  const schemaInfo = await getSchemaInfo(context2.deployedGameInfo?.schemaSnapshot);
8542
- let secrets = {};
8543
- let secretKeys = [];
8544
- if (context2.existingGame) {
8545
- const secretsResult = await fetchSecretsForDeployment(config.slug);
8546
- secrets = secretsResult.secrets;
8547
- secretKeys = secretsResult.keys;
8548
- }
8549
8448
  const integrationKeys = getIntegrationKeys(fullConfig.integrations);
8550
8449
  const integrationsHash = fullConfig.integrations ? hashContent(fullConfig.integrations) : null;
8551
8450
  return {
8552
8451
  bindings,
8553
8452
  schemaInfo,
8554
- secrets,
8555
- secretKeys,
8556
8453
  integrationKeys,
8557
8454
  integrationsHash
8558
8455
  };
@@ -8565,8 +8462,7 @@ async function deployGame(context2, shouldUploadBuild, shouldDeployBackend) {
8565
8462
  backendBundle = {
8566
8463
  ...bundle,
8567
8464
  bindings: Object.keys(deploymentPrep.bindings).length > 0 ? deploymentPrep.bindings : void 0,
8568
- schema: deploymentPrep.schemaInfo || void 0,
8569
- secrets: Object.keys(deploymentPrep.secrets).length > 0 ? deploymentPrep.secrets : void 0
8465
+ schema: deploymentPrep.schemaInfo || void 0
8570
8466
  };
8571
8467
  const [serverSize, dbSize] = await Promise.all([
8572
8468
  getDirectorySize(join31(context2.projectPath, SERVER_ROOT_DIRECTORY)),
@@ -8582,7 +8478,6 @@ async function deployGame(context2, shouldUploadBuild, shouldDeployBackend) {
8582
8478
  schemaSnapshot: deploymentPrep.schemaInfo?.hash ? JSON.parse(deploymentPrep.schemaInfo.hash) : void 0,
8583
8479
  integrationKeys: deploymentPrep.integrationKeys,
8584
8480
  integrationsHash: deploymentPrep.integrationsHash,
8585
- deployedSecrets: deploymentPrep.secretKeys,
8586
8481
  deployedAt: (/* @__PURE__ */ new Date()).toISOString()
8587
8482
  };
8588
8483
  } else if (buildFile && !shouldDeployBackend) {
@@ -9421,8 +9316,155 @@ async function uploadFilesLocal(files, prefix, uploadFn) {
9421
9316
  return files.length;
9422
9317
  }
9423
9318
 
9319
+ // src/lib/logs/display.ts
9320
+ import { blue as blue3, bold as bold7, cyan as cyan7, dim as dim9, gray as gray2, green as green7, red as red5, yellow as yellow5 } from "colorette";
9321
+ var LEVEL_COLORS = {
9322
+ log: gray2,
9323
+ info: blue3,
9324
+ warn: yellow5,
9325
+ error: red5,
9326
+ debug: dim9
9327
+ };
9328
+ var OUTCOME_COLORS = {
9329
+ ok: green7,
9330
+ exception: red5,
9331
+ exceededCpu: red5,
9332
+ exceededMemory: red5,
9333
+ canceled: yellow5,
9334
+ scriptNotFound: red5,
9335
+ unknown: gray2
9336
+ };
9337
+ function formatTime2(timestamp5) {
9338
+ const date = new Date(timestamp5);
9339
+ return date.toLocaleTimeString("en-US", {
9340
+ hour: "numeric",
9341
+ minute: "2-digit",
9342
+ second: "2-digit",
9343
+ hour12: true
9344
+ });
9345
+ }
9346
+ var HTTP_STATUS_TEXT = {
9347
+ 200: "OK",
9348
+ 201: "Created",
9349
+ 204: "No Content",
9350
+ 301: "Moved Permanently",
9351
+ 302: "Found",
9352
+ 304: "Not Modified",
9353
+ 400: "Bad Request",
9354
+ 401: "Unauthorized",
9355
+ 403: "Forbidden",
9356
+ 404: "Not Found",
9357
+ 405: "Method Not Allowed",
9358
+ 409: "Conflict",
9359
+ 422: "Unprocessable Entity",
9360
+ 429: "Too Many Requests",
9361
+ 500: "Internal Server Error",
9362
+ 502: "Bad Gateway",
9363
+ 503: "Service Unavailable",
9364
+ 504: "Gateway Timeout"
9365
+ };
9366
+ function formatStatus(status) {
9367
+ const text5 = HTTP_STATUS_TEXT[status] ?? "";
9368
+ const colorFn = status >= 500 ? red5 : status >= 400 ? yellow5 : status >= 300 ? blue3 : status >= 200 ? green7 : gray2;
9369
+ const boldCode = colorFn(bold7(String(status)));
9370
+ return text5 ? `${boldCode} ${colorFn(text5)}` : boldCode;
9371
+ }
9372
+ function stripLogPrefix(message) {
9373
+ const prefixPattern = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z?\s*)?(LOG|INFO|WARN|ERROR|DEBUG)\s*/i;
9374
+ return message.replace(prefixPattern, "");
9375
+ }
9376
+ function formatValue(value) {
9377
+ if (typeof value === "string") return stripLogPrefix(value);
9378
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
9379
+ if (value === null) return "null";
9380
+ if (value === void 0) return "undefined";
9381
+ try {
9382
+ return JSON.stringify(value, null, 2);
9383
+ } catch {
9384
+ return String(value);
9385
+ }
9386
+ }
9387
+ function displayLogEntry(entry) {
9388
+ const time = dim9(formatTime2(entry.timestamp));
9389
+ const outcomeColor = OUTCOME_COLORS[entry.outcome] ?? gray2;
9390
+ const outcome = outcomeColor(entry.outcome.toUpperCase());
9391
+ if (entry.request) {
9392
+ const method = cyan7(bold7(entry.request.method));
9393
+ const urlObj = new URL(entry.request.url);
9394
+ const url = urlObj.pathname + urlObj.search;
9395
+ const status = entry.response?.status;
9396
+ let statusPart = "";
9397
+ if (status) {
9398
+ statusPart = formatStatus(status);
9399
+ if (entry.outcome !== "ok") {
9400
+ statusPart += ` ${outcome}`;
9401
+ }
9402
+ } else {
9403
+ statusPart = outcome;
9404
+ }
9405
+ logger.raw(`${time} ${method} ${url} ${statusPart}`);
9406
+ }
9407
+ for (const log2 of entry.logs) {
9408
+ const levelColor = LEVEL_COLORS[log2.level] ?? gray2;
9409
+ const level = levelColor(bold7(log2.level.toUpperCase()));
9410
+ const message = log2.message.map(formatValue).join(" ");
9411
+ logger.raw(`${time} ${level} ${message}`);
9412
+ }
9413
+ for (const exception of entry.exceptions) {
9414
+ const level = red5(bold7("ERROR"));
9415
+ logger.raw(`${time} ${level} ${exception.name}: ${exception.message}`);
9416
+ }
9417
+ }
9418
+
9419
+ // src/lib/logs/stream.ts
9420
+ import WebSocket from "ws";
9421
+ function buildLogStreamUrl(options) {
9422
+ const { workerId, token, history } = options;
9423
+ const wsBaseUrl = LOG_COLLECTOR_URL.replace("https://", "wss://");
9424
+ const url = new URL(`${wsBaseUrl}/logs/${workerId}/stream`);
9425
+ url.searchParams.set("token", token);
9426
+ if (history) url.searchParams.set("history", "true");
9427
+ return url.toString();
9428
+ }
9429
+ async function connectToLogStream(config) {
9430
+ const { url, slug, environment } = config;
9431
+ const ws = new WebSocket(url);
9432
+ ws.on("open", () => {
9433
+ logger.success(`Connected to ${environment} logs for "${slug}"`);
9434
+ logger.newLine();
9435
+ logger.dim("Press ctrl+c to stop", 1);
9436
+ logger.newLine();
9437
+ });
9438
+ ws.on("message", (data) => {
9439
+ try {
9440
+ const entry = JSON.parse(data.toString());
9441
+ displayLogEntry(entry);
9442
+ } catch {
9443
+ logger.raw(data.toString());
9444
+ }
9445
+ });
9446
+ ws.on("error", (error) => {
9447
+ logger.newLine();
9448
+ logger.error(`WebSocket error: ${error.message}`);
9449
+ process.exit(1);
9450
+ });
9451
+ ws.on("close", (code, reason) => {
9452
+ logger.newLine();
9453
+ if (code !== 1e3) {
9454
+ logger.warn(`Connection closed: ${code} ${reason.toString()}`);
9455
+ }
9456
+ process.exit(0);
9457
+ });
9458
+ process.on("SIGINT", () => {
9459
+ logger.newLine();
9460
+ ws.close(1e3, "User interrupted");
9461
+ });
9462
+ await new Promise(() => {
9463
+ });
9464
+ }
9465
+
9424
9466
  // src/lib/timeback/display.ts
9425
- import { bold as bold7, greenBright as greenBright2, redBright as redBright2 } from "colorette";
9467
+ import { bold as bold8, greenBright as greenBright2, redBright as redBright2 } from "colorette";
9426
9468
  function displayIntegrationDetails(integration) {
9427
9469
  logger.highlight("Verified Course");
9428
9470
  logger.newLine();
@@ -9459,9 +9501,9 @@ function displayIntegrationList(integrations, statusByCourseId, options) {
9459
9501
  if (showStatusColumn) {
9460
9502
  if (options?.staticLabel) {
9461
9503
  const coloredLabel = options.staticLabelColor ? options.staticLabelColor(options.staticLabel) : greenBright2(options.staticLabel);
9462
- statusText = bold7(coloredLabel);
9504
+ statusText = bold8(coloredLabel);
9463
9505
  } else if (statusByCourseId && integration.courseId in statusByCourseId) {
9464
- statusText = statusByCourseId[integration.courseId] ? bold7(greenBright2(successLabel)) : bold7(redBright2(failureLabel));
9506
+ statusText = statusByCourseId[integration.courseId] ? bold8(greenBright2(successLabel)) : bold8(redBright2(failureLabel));
9465
9507
  }
9466
9508
  }
9467
9509
  const row = {
@@ -9495,7 +9537,7 @@ function displayCleanupWarning(integrations, gameName, logger2) {
9495
9537
  }
9496
9538
 
9497
9539
  // src/lib/timeback/setup.ts
9498
- import { bold as bold8 } from "colorette";
9540
+ import { bold as bold9 } from "colorette";
9499
9541
  function displaySetupDryRun(derivedRequest, env, logger2) {
9500
9542
  logger2.highlight("TimeBack Setup [DRY RUN]");
9501
9543
  logger2.newLine();
@@ -9525,7 +9567,7 @@ function displaySetupDryRun(derivedRequest, env, logger2) {
9525
9567
  logger2.admonition("tip", "Ready for setup", [
9526
9568
  "Your Timeback configuration has been validated!",
9527
9569
  "",
9528
- `Run without \`--dry-run\` to create ${bold8(`${derivedRequest.courses.length} course(s)`)} in ${bold8(env || "staging")}`
9570
+ `Run without \`--dry-run\` to create ${bold9(`${derivedRequest.courses.length} course(s)`)} in ${bold9(env || "staging")}`
9529
9571
  ]);
9530
9572
  logger2.newLine();
9531
9573
  }
@@ -9631,7 +9673,7 @@ function displayVerificationSummary(result, options) {
9631
9673
  }
9632
9674
 
9633
9675
  // src/lib/domains/display.ts
9634
- import { cyanBright, gray as gray2, greenBright as greenBright3, redBright as redBright3, yellowBright as yellowBright2 } from "colorette";
9676
+ import { cyanBright, gray as gray3, greenBright as greenBright3, redBright as redBright3, yellowBright as yellowBright2 } from "colorette";
9635
9677
  function colorizeStatus(status) {
9636
9678
  switch (status) {
9637
9679
  case "active":
@@ -9648,7 +9690,7 @@ function colorizeStatus(status) {
9648
9690
  case "pending_deletion":
9649
9691
  return redBright3(status);
9650
9692
  default:
9651
- return gray2(status);
9693
+ return gray3(status);
9652
9694
  }
9653
9695
  }
9654
9696
  function displayDomainStatus(hostname, domain) {
@@ -9906,7 +9948,7 @@ initCommand.addCommand(configCommand);
9906
9948
  // src/commands/login.ts
9907
9949
  import { userInfo } from "os";
9908
9950
  import { input as input4, password, select as select6 } from "@inquirer/prompts";
9909
- import { bold as bold9, dim as dim9, whiteBright } from "colorette";
9951
+ import { bold as bold10, dim as dim10, whiteBright } from "colorette";
9910
9952
  import { Command as Command3 } from "commander";
9911
9953
  import open from "open";
9912
9954
  var loginCommand = new Command3("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) => {
@@ -9920,12 +9962,12 @@ var loginCommand = new Command3("login").description("Authenticate with Playcade
9920
9962
  const otherEnv = environment === "staging" ? "production" : "staging";
9921
9963
  const otherProfile = await getProfile(otherEnv, profileName);
9922
9964
  logger.admonition("note", "Hello again", [
9923
- bold9("You're already logged in"),
9965
+ bold10("You're already logged in"),
9924
9966
  "",
9925
- dim9("Email: ") + bold9(email2),
9926
- dim9("Environment: ") + bold9(environment),
9927
- dim9("Profile: ") + bold9(profileName),
9928
- ...otherProfile ? [dim9("Other Profile: ") + bold9(otherEnv)] : []
9967
+ dim10("Email: ") + bold10(email2),
9968
+ dim10("Environment: ") + bold10(environment),
9969
+ dim10("Profile: ") + bold10(profileName),
9970
+ ...otherProfile ? [dim10("Other Profile: ") + bold10(otherEnv)] : []
9929
9971
  ]);
9930
9972
  logger.newLine();
9931
9973
  return;
@@ -10057,7 +10099,7 @@ async function handleSsoLogin(environment, profileName) {
10057
10099
  logger.admonition("warning", "Your API Key", [
10058
10100
  "Save this key securely - it will not be shown again!",
10059
10101
  "",
10060
- whiteBright(bold9(apiKeyResult.apiKey))
10102
+ whiteBright(bold10(apiKeyResult.apiKey))
10061
10103
  ]);
10062
10104
  logger.newLine();
10063
10105
  await runStep(
@@ -10123,9 +10165,53 @@ var logoutCommand = new Command4("logout").description("Log out from Playcademy"
10123
10165
  }
10124
10166
  });
10125
10167
 
10126
- // src/commands/me/index.ts
10168
+ // src/commands/logs/index.ts
10127
10169
  import { Command as Command5 } from "commander";
10128
- var meCommand = new Command5("me").description("Display current user information").option(
10170
+ import { ApiError as ApiError3 } from "@playcademy/sdk/internal";
10171
+ async function runLogs(slugArg, options) {
10172
+ try {
10173
+ logger.newLine();
10174
+ const environment = ensureEnvironment(options.env);
10175
+ let slug;
10176
+ if (slugArg) {
10177
+ slug = slugArg;
10178
+ } else {
10179
+ try {
10180
+ await findConfigPath();
10181
+ const config = await loadConfig();
10182
+ slug = getSlugFromConfig(config);
10183
+ } catch {
10184
+ logger.error("No slug provided and no config file found");
10185
+ logger.newLine();
10186
+ process.exit(1);
10187
+ }
10188
+ }
10189
+ const client = await requireAuthenticatedClient();
10190
+ let token;
10191
+ let workerId;
10192
+ try {
10193
+ const result = await client.dev.games.logs.getToken(slug, environment);
10194
+ token = result.token;
10195
+ workerId = result.workerId;
10196
+ } catch (error) {
10197
+ if (error instanceof ApiError3 && error.status === 404) {
10198
+ logger.error(`Project not found: "${slug}"`);
10199
+ logger.newLine();
10200
+ process.exit(1);
10201
+ }
10202
+ throw error;
10203
+ }
10204
+ const url = buildLogStreamUrl({ workerId, token, history: options.history });
10205
+ await connectToLogStream({ url, slug, environment });
10206
+ } catch (error) {
10207
+ logAndExit(error, { prefix: "Failed to stream logs" });
10208
+ }
10209
+ }
10210
+ var logsCommand = new Command5("logs").description("Stream real-time logs from a deployed app").argument("[slug]", "Game slug (uses config file if not provided)").option("--env <environment>", "Environment (production or staging)").option("--history", "Include recent log history (default: false)").action(runLogs);
10211
+
10212
+ // src/commands/me/index.ts
10213
+ import { Command as Command6 } from "commander";
10214
+ var meCommand = new Command6("me").description("Display current user information").option(
10129
10215
  "--env <environment>",
10130
10216
  "Environment to check user information from (staging or production)"
10131
10217
  ).action(async (options) => {
@@ -10165,21 +10251,21 @@ var meCommand = new Command5("me").description("Display current user information
10165
10251
 
10166
10252
  // src/commands/update/index.ts
10167
10253
  import { execSync as execSync8 } from "child_process";
10168
- import { bold as bold10 } from "colorette";
10169
- import { Command as Command6 } from "commander";
10254
+ import { bold as bold11 } from "colorette";
10255
+ import { Command as Command7 } from "commander";
10170
10256
  var updateCommands = {
10171
10257
  bun: "bun install -g playcademy@latest",
10172
10258
  npm: "npm install -g playcademy@latest",
10173
10259
  pnpm: "pnpm add -g playcademy@latest",
10174
10260
  yarn: "yarn global add playcademy@latest"
10175
10261
  };
10176
- var updateCommand = new Command6("update").description("Update the Playcademy CLI to the latest version").action(async () => {
10262
+ var updateCommand = new Command7("update").description("Update the Playcademy CLI to the latest version").action(async () => {
10177
10263
  const runnerPath = executorIsPackageRunner();
10178
10264
  if (runnerPath) {
10179
10265
  const runnerName = getPackageRunnerName(runnerPath);
10180
10266
  logger.newLine();
10181
10267
  logger.admonition("info", "Package Runner Detected", [
10182
- `You are running the CLI via package runner (${bold10(runnerName)}).`,
10268
+ `You are running the CLI via package runner (${bold11(runnerName)}).`,
10183
10269
  "",
10184
10270
  "Package runners use the latest version of the CLI by default, but may cache older versions.",
10185
10271
  `To ensure you run the package's latest version: \`${runnerName} playcademy@latest\``
@@ -10205,7 +10291,7 @@ var updateCommand = new Command6("update").description("Update the Playcademy CL
10205
10291
  });
10206
10292
 
10207
10293
  // src/commands/deploy/index.ts
10208
- import { Command as Command7 } from "commander";
10294
+ import { Command as Command8 } from "commander";
10209
10295
  async function executeDeployment(plan, context2, environment) {
10210
10296
  const result = await deployGame(context2, plan.shouldUploadBuild, plan.shouldDeployBackend);
10211
10297
  const game = result.game;
@@ -10267,7 +10353,7 @@ async function runDeployment(options) {
10267
10353
  reportDeploymentSuccess(result, context2);
10268
10354
  }
10269
10355
  }
10270
- var deployCommand = new Command7("deploy").description("Deploy your project to Playcademy").option("-c, --config <path>", "Path to configuration file").option("-n, --name <name>", "Display name").option("-d, --description <desc>", "App description").option("-e, --emoji <emoji>", "App emoji").option("-b, --build <path>", "Path to zip file").option("--env <environment>", "Deployment environment (production or staging)").option("--backend", "Deploy backend (API routes + database + integrations)").option("--no-backend", "Skip backend deployment").option("--force-backend", "Force backend deployment even if no changes detected").option("--dry-run", "Validate configuration without deploying").option("-v, --verbose", "Show verbose output").option("--debug", "Show debug information (change detection, hashes, etc.)").action(async (options) => {
10356
+ var deployCommand = new Command8("deploy").description("Deploy your project to Playcademy").option("-c, --config <path>", "Path to configuration file").option("-n, --name <name>", "Display name").option("-d, --description <desc>", "App description").option("-e, --emoji <emoji>", "App emoji").option("-b, --build <path>", "Path to zip file").option("--env <environment>", "Deployment environment (production or staging)").option("--backend", "Deploy backend (API routes + database + integrations)").option("--no-backend", "Skip backend deployment").option("--force-backend", "Force backend deployment even if no changes detected").option("--dry-run", "Validate configuration without deploying").option("-v, --verbose", "Show verbose output").option("--debug", "Show debug information (change detection, hashes, etc.)").action(async (options) => {
10271
10357
  try {
10272
10358
  await runDeployment(options);
10273
10359
  } catch (error) {
@@ -10281,12 +10367,12 @@ var deployCommand = new Command7("deploy").description("Deploy your project to P
10281
10367
  });
10282
10368
 
10283
10369
  // src/commands/projects/index.ts
10284
- import { Command as Command10 } from "commander";
10370
+ import { Command as Command11 } from "commander";
10285
10371
 
10286
10372
  // src/commands/projects/delete.ts
10287
10373
  import { confirm as confirm7, input as input5, select as select7 } from "@inquirer/prompts";
10288
- import { Command as Command8 } from "commander";
10289
- var deleteCommand = new Command8("delete").alias("rm").alias("remove").description("Delete a project").argument("[slug]", "Project slug to delete").option("-f, --force", "Skip confirmation prompt").option("--env <environment>", "Environment to delete project from (staging or production)").action(async (slug, options) => {
10374
+ import { Command as Command9 } from "commander";
10375
+ var deleteCommand = new Command9("delete").alias("rm").alias("remove").description("Delete a project").argument("[slug]", "Project slug to delete").option("-f, --force", "Skip confirmation prompt").option("--env <environment>", "Environment to delete project from (staging or production)").action(async (slug, options) => {
10290
10376
  const { env } = options;
10291
10377
  try {
10292
10378
  const environment = ensureEnvironment(env);
@@ -10378,8 +10464,8 @@ var deleteCommand = new Command8("delete").alias("rm").alias("remove").descripti
10378
10464
  });
10379
10465
 
10380
10466
  // src/commands/projects/list.ts
10381
- import { Command as Command9 } from "commander";
10382
- var listCommand = new Command9("list").alias("ls").description("List your projects").option("--env <environment>", "Environment to list projects from (staging or production)").action(async (options) => {
10467
+ import { Command as Command10 } from "commander";
10468
+ var listCommand = new Command10("list").alias("ls").description("List your projects").option("--env <environment>", "Environment to list projects from (staging or production)").action(async (options) => {
10383
10469
  const { env } = options;
10384
10470
  try {
10385
10471
  const environment = ensureEnvironment(env);
@@ -10413,16 +10499,16 @@ var listCommand = new Command9("list").alias("ls").description("List your projec
10413
10499
  });
10414
10500
 
10415
10501
  // src/commands/projects/index.ts
10416
- var projectsCommand = new Command10("projects").description("Manage deployed projects");
10502
+ var projectsCommand = new Command11("projects").description("Manage deployed projects");
10417
10503
  projectsCommand.addCommand(listCommand);
10418
10504
  projectsCommand.addCommand(deleteCommand);
10419
10505
 
10420
10506
  // src/commands/dev/index.ts
10421
- import { Command as Command13 } from "commander";
10507
+ import { Command as Command14 } from "commander";
10422
10508
 
10423
10509
  // src/commands/dev/apply.ts
10424
- import { Command as Command11 } from "commander";
10425
- var applyCommand = new Command11("apply").description("Apply for developer status").option(
10510
+ import { Command as Command12 } from "commander";
10511
+ var applyCommand = new Command12("apply").description("Apply for developer status").option(
10426
10512
  "--env <environment>",
10427
10513
  "Environment to apply for developer status in (staging or production)"
10428
10514
  ).action(async (options) => {
@@ -10473,8 +10559,8 @@ var applyCommand = new Command11("apply").description("Apply for developer statu
10473
10559
  });
10474
10560
 
10475
10561
  // src/commands/dev/get-status.ts
10476
- import { Command as Command12 } from "commander";
10477
- var getStatusCommand = new Command12("status").description("Check your developer status").option(
10562
+ import { Command as Command13 } from "commander";
10563
+ var getStatusCommand = new Command13("status").description("Check your developer status").option(
10478
10564
  "--env <environment>",
10479
10565
  "Environment to check developer status from (staging or production)"
10480
10566
  ).action(async (options) => {
@@ -10517,7 +10603,7 @@ var getStatusCommand = new Command12("status").description("Check your developer
10517
10603
  });
10518
10604
 
10519
10605
  // package.json
10520
- var version2 = "0.14.32";
10606
+ var version2 = "0.14.34";
10521
10607
 
10522
10608
  // src/commands/dev/server.ts
10523
10609
  function setupCleanupHandlers(workspace, getServer) {
@@ -10603,12 +10689,12 @@ async function runDevServer(options) {
10603
10689
  }
10604
10690
 
10605
10691
  // src/commands/dev/index.ts
10606
- var devCommand = new Command13("dev").description("Start local backend development server").option("-p, --port <port>", "Backend server port", String(DEFAULT_PORTS.BACKEND)).option("--no-reload", "Disable hot reload").option("--no-logger", "Disable HTTP request logging").option("-q, --quiet", "Quiet mode").action(runDevServer);
10692
+ var devCommand = new Command14("dev").description("Start local backend development server").option("-p, --port <port>", "Backend server port", String(DEFAULT_PORTS.BACKEND)).option("--no-reload", "Disable hot reload").option("--no-logger", "Disable HTTP request logging").option("-q, --quiet", "Quiet mode").action(runDevServer);
10607
10693
  devCommand.addCommand(applyCommand);
10608
10694
  devCommand.addCommand(getStatusCommand);
10609
10695
 
10610
10696
  // src/commands/api/index.ts
10611
- import { Command as Command14 } from "commander";
10697
+ import { Command as Command15 } from "commander";
10612
10698
 
10613
10699
  // src/commands/api/init.ts
10614
10700
  import { input as input6 } from "@inquirer/prompts";
@@ -10697,19 +10783,19 @@ async function runApiInit() {
10697
10783
  }
10698
10784
 
10699
10785
  // src/commands/api/index.ts
10700
- var apiCommand = new Command14("api").description("Custom API routes management");
10786
+ var apiCommand = new Command15("api").description("Custom API routes management");
10701
10787
  apiCommand.command("init").description("Add custom API routes to your project").action(runApiInit);
10702
10788
 
10703
10789
  // src/commands/auth/index.ts
10704
- import { Command as Command17 } from "commander";
10790
+ import { Command as Command18 } from "commander";
10705
10791
 
10706
10792
  // src/commands/auth/add.ts
10707
10793
  init_file_loader();
10708
10794
  import { writeFileSync as writeFileSync14 } from "fs";
10709
10795
  import { join as join35 } from "path";
10710
- import { bold as bold11 } from "colorette";
10711
- import { Command as Command15 } from "commander";
10712
- var addAuthCommand = new Command15("add").description("Add an authentication provider to your project").argument("<provider>", "Provider to add: email | github | google").addHelpText(
10796
+ import { bold as bold12 } from "colorette";
10797
+ import { Command as Command16 } from "commander";
10798
+ var addAuthCommand = new Command16("add").description("Add an authentication provider to your project").argument("<provider>", "Provider to add: email | github | google").addHelpText(
10713
10799
  "after",
10714
10800
  `
10715
10801
  Examples:
@@ -10775,16 +10861,16 @@ Examples:
10775
10861
  PLACEHOLDER_APP_URL
10776
10862
  ).replace("{provider}", provider);
10777
10863
  logger.admonition("tip", "Next Steps", [
10778
- bold11(`1. Add callback URL to your ${providerDisplayName} OAuth app:`),
10864
+ bold12(`1. Add callback URL to your ${providerDisplayName} OAuth app:`),
10779
10865
  "",
10780
10866
  ` <${callbackUrl}>`,
10781
10867
  "",
10782
- bold11("2. Add credentials to <.env> (for local development):"),
10868
+ bold12("2. Add credentials to <.env> (for local development):"),
10783
10869
  "",
10784
10870
  ` \`${provider.toUpperCase()}_CLIENT_ID=[your-id]\``,
10785
10871
  ` \`${provider.toUpperCase()}_CLIENT_SECRET=[your-secret]\``,
10786
10872
  "",
10787
- bold11("3. Set production secrets (for deployment):"),
10873
+ bold12("3. Set production secrets (for deployment):"),
10788
10874
  "",
10789
10875
  ` \`playcademy secret set ${provider.toUpperCase()}_CLIENT_ID=[your-id]\``,
10790
10876
  ` \`playcademy secret set ${provider.toUpperCase()}_CLIENT_SECRET=[your-secret]\``
@@ -10799,9 +10885,9 @@ Examples:
10799
10885
 
10800
10886
  // src/commands/auth/init.ts
10801
10887
  import { confirm as confirm8 } from "@inquirer/prompts";
10802
- import { Command as Command16 } from "commander";
10888
+ import { Command as Command17 } from "commander";
10803
10889
  var sampleCustomRouteTemplate2 = loadTemplateString("api/sample-custom.ts");
10804
- var initAuthCommand = new Command16("init").description("Add authentication to your project").action(async () => {
10890
+ var initAuthCommand = new Command17("init").description("Add authentication to your project").action(async () => {
10805
10891
  try {
10806
10892
  logger.newLine();
10807
10893
  const configPath = await requireConfigFile();
@@ -10903,14 +10989,14 @@ var initAuthCommand = new Command16("init").description("Add authentication to y
10903
10989
  });
10904
10990
 
10905
10991
  // src/commands/auth/index.ts
10906
- var authCommand = new Command17("auth").description("Manage authentication for your project").action(() => {
10992
+ var authCommand = new Command18("auth").description("Manage authentication for your project").action(() => {
10907
10993
  authCommand.help();
10908
10994
  });
10909
10995
  authCommand.addCommand(initAuthCommand);
10910
10996
  authCommand.addCommand(addAuthCommand);
10911
10997
 
10912
10998
  // src/commands/db/index.ts
10913
- import { Command as Command18 } from "commander";
10999
+ import { Command as Command19 } from "commander";
10914
11000
 
10915
11001
  // src/commands/db/diff.ts
10916
11002
  async function runDbDiff() {
@@ -11028,7 +11114,7 @@ async function runDbInit() {
11028
11114
  import { existsSync as existsSync25 } from "fs";
11029
11115
  import { join as join36 } from "path";
11030
11116
  import { confirm as confirm9, input as input8 } from "@inquirer/prompts";
11031
- import { bold as bold12, redBright as redBright4, underline as underline3 } from "colorette";
11117
+ import { bold as bold13, redBright as redBright4, underline as underline3 } from "colorette";
11032
11118
  import { Miniflare as Miniflare2 } from "miniflare";
11033
11119
  async function runDbResetRemote(options) {
11034
11120
  const environment = ensureEnvironment(options.env);
@@ -11046,9 +11132,9 @@ async function runDbResetRemote(options) {
11046
11132
  const game = await client.games.fetch(deployedGame.gameId);
11047
11133
  logger.newLine();
11048
11134
  logger.admonition("warning", "DESTRUCTIVE OPERATION", [
11049
- `Are you sure you want to ${redBright4(underline3(bold12("DELETE ALL DATA")))} in your ${environment} database?`,
11135
+ `Are you sure you want to ${redBright4(underline3(bold13("DELETE ALL DATA")))} in your ${environment} database?`,
11050
11136
  `All tables will be dropped and recreated from schema.`,
11051
- `This action is irreversible and ${underline3(bold12("cannot be undone"))}.`
11137
+ `This action is irreversible and ${underline3(bold13("cannot be undone"))}.`
11052
11138
  ]);
11053
11139
  logger.newLine();
11054
11140
  const confirmed = await confirm9({
@@ -11215,7 +11301,7 @@ async function runDbSchema(options = {}) {
11215
11301
  import { existsSync as existsSync26 } from "fs";
11216
11302
  import { join as join37 } from "path";
11217
11303
  import { confirm as confirm10, input as input9 } from "@inquirer/prompts";
11218
- import { bold as bold13, dim as dim10, redBright as redBright5, underline as underline4 } from "colorette";
11304
+ import { bold as bold14, dim as dim11, redBright as redBright5, underline as underline4 } from "colorette";
11219
11305
  import { Miniflare as Miniflare3 } from "miniflare";
11220
11306
  async function runDbResetRemote2(gameSlug, environment) {
11221
11307
  const client = await requireAuthenticatedClient();
@@ -11259,8 +11345,8 @@ async function runDbSeedRemote(seedFile, options) {
11259
11345
  logger.newLine();
11260
11346
  if (willReset) {
11261
11347
  logger.admonition("warning", "Remote Database Seeding", [
11262
- `Are you sure you want to ${redBright5(underline4(bold13("DELETE ALL DATA")))} in your ${environment} database?`,
11263
- `This action is irreversible and ${underline4(bold13("cannot be undone"))}.`,
11348
+ `Are you sure you want to ${redBright5(underline4(bold14("DELETE ALL DATA")))} in your ${environment} database?`,
11349
+ `This action is irreversible and ${underline4(bold14("cannot be undone"))}.`,
11264
11350
  `Run this command with \`--no-reset\` if you want to seed without resetting.`
11265
11351
  ]);
11266
11352
  } else {
@@ -11345,7 +11431,7 @@ async function runDbSeedRemote(seedFile, options) {
11345
11431
  displaySeedLogs(seedResult.logs, "error");
11346
11432
  }
11347
11433
  logger.newLine();
11348
- logger.error(`Seeding failed ${dim10(`[${Math.round(duration)}ms]`)}`);
11434
+ logger.error(`Seeding failed ${dim11(`[${Math.round(duration)}ms]`)}`);
11349
11435
  logger.newLine();
11350
11436
  process.exit(1);
11351
11437
  }
@@ -11355,7 +11441,7 @@ async function runDbSeedRemote(seedFile, options) {
11355
11441
  }
11356
11442
  logger.newLine();
11357
11443
  logger.success(
11358
- `${capitalize(environment)} database seeded ${dim10(`[${Math.round(duration)}ms]`)}`
11444
+ `${capitalize(environment)} database seeded ${dim11(`[${Math.round(duration)}ms]`)}`
11359
11445
  );
11360
11446
  logger.newLine();
11361
11447
  }
@@ -11440,7 +11526,7 @@ async function runDbSeed(options = {}) {
11440
11526
  }
11441
11527
 
11442
11528
  // src/commands/db/index.ts
11443
- var dbCommand = new Command18("db").description("Database management commands");
11529
+ var dbCommand = new Command19("db").description("Database management commands");
11444
11530
  dbCommand.command("init").description("Add database to your project").action(runDbInit);
11445
11531
  dbCommand.command("reset").description("Reset database").option("--remote", "Reset remote deployed database").option("--env <environment>", "Environment: staging (default) or production").option("-f, --force", "Skip confirmation prompt (local only)").option("--debug", "Enable debug mode").action(
11446
11532
  (options) => runDbReset({
@@ -11463,7 +11549,7 @@ dbCommand.command("diff").description("Show schema changes since last deployment
11463
11549
  dbCommand.command("schema").description("Print full schema SQL that would be pushed after reset").option("--raw", "Output raw SQL only (for piping to files)").option("--full", "Show full schema hash").action((options) => runDbSchema({ raw: options.raw, full: options.full }));
11464
11550
 
11465
11551
  // src/commands/kv/index.ts
11466
- import { Command as Command19 } from "commander";
11552
+ import { Command as Command20 } from "commander";
11467
11553
 
11468
11554
  // src/commands/kv/clear.ts
11469
11555
  import { join as join38 } from "path";
@@ -12400,7 +12486,7 @@ async function runKVStats(options = {}) {
12400
12486
  }
12401
12487
 
12402
12488
  // src/commands/kv/index.ts
12403
- var kvCommand = new Command19("kv").description("Manage KV storage integration").action(() => {
12489
+ var kvCommand = new Command20("kv").description("Manage KV storage integration").action(() => {
12404
12490
  kvCommand.help();
12405
12491
  });
12406
12492
  kvCommand.command("init").description("Add KV storage integration to your project").action(runKVInit);
@@ -12438,7 +12524,7 @@ kvCommand.command("seed <file>").description("Seed KV namespace with key-value p
12438
12524
  ).action(runKVSeed);
12439
12525
 
12440
12526
  // src/commands/bucket/index.ts
12441
- import { Command as Command20 } from "commander";
12527
+ import { Command as Command21 } from "commander";
12442
12528
 
12443
12529
  // src/commands/bucket/bulk.ts
12444
12530
  import { existsSync as existsSync27, statSync as statSync3 } from "fs";
@@ -13250,7 +13336,7 @@ async function runBucketPut(key, filePath, options = {}) {
13250
13336
  }
13251
13337
 
13252
13338
  // src/commands/bucket/index.ts
13253
- var bucketCommand = new Command20("bucket").description("Manage bucket storage integration").action(() => {
13339
+ var bucketCommand = new Command21("bucket").description("Manage bucket storage integration").action(() => {
13254
13340
  bucketCommand.help();
13255
13341
  });
13256
13342
  bucketCommand.command("init").description("Add bucket storage integration to your project").action(runBucketInit);
@@ -13284,7 +13370,7 @@ bucketCommand.command("bulk <directory>").description(
13284
13370
  ).action(runBucketBulk);
13285
13371
 
13286
13372
  // src/commands/domain/index.ts
13287
- import { Command as Command21 } from "commander";
13373
+ import { Command as Command22 } from "commander";
13288
13374
 
13289
13375
  // src/commands/domain/add.ts
13290
13376
  import { underline as underline5 } from "colorette";
@@ -13480,20 +13566,20 @@ async function runDomainVerify(hostname, options) {
13480
13566
  }
13481
13567
 
13482
13568
  // src/commands/domain/index.ts
13483
- var domainCommand = new Command21("domain").description("Custom domain management");
13569
+ var domainCommand = new Command22("domain").description("Custom domain management");
13484
13570
  domainCommand.command("add <hostname>").description("Add a custom domain to your project").option("--env <env>", "Environment (staging or production)", "staging").action(runDomainAdd);
13485
13571
  domainCommand.command("list").description("List custom domains for your project").option("--env <env>", "Environment (staging or production)", "staging").option("--json", "Output as JSON").option("--raw", "Output hostnames only").alias("ls").action(runDomainList);
13486
13572
  domainCommand.command("verify <hostname>").description("Check custom domain validation status").option("--env <env>", "Environment (staging or production)", "staging").option("--json", "Output as JSON").option("--refresh", "Re-trigger domain validation", false).alias("status").action(runDomainVerify);
13487
13573
  domainCommand.command("delete <hostname>").description("Remove a custom domain").option("--env <env>", "Environment (staging or production)", "staging").option("-f, --force", "Skip confirmation prompt").alias("rm").alias("remove").action(runDomainDelete);
13488
13574
 
13489
13575
  // src/commands/secret/index.ts
13490
- import { Command as Command25 } from "commander";
13576
+ import { Command as Command26 } from "commander";
13491
13577
 
13492
13578
  // src/commands/secret/delete.ts
13493
13579
  import { confirm as confirm14 } from "@inquirer/prompts";
13494
- import { Command as Command22 } from "commander";
13495
- var deleteCommand2 = new Command22("delete").description("Delete a project secret").argument("<key>", "Secret key to delete").option("--env <environment>", "Environment (staging or production)").option("-f, --force", "Skip confirmation").option("--no-deploy", "Skip deployment prompt").action(async (key, options) => {
13496
- const { env, deploy: shouldPromptDeploy = true } = options;
13580
+ import { Command as Command23 } from "commander";
13581
+ var deleteCommand2 = new Command23("delete").description("Delete a project secret").argument("<key>", "Secret key to delete").option("--env <environment>", "Environment (staging or production)").option("-f, --force", "Skip confirmation").action(async (key, options) => {
13582
+ const { env } = options;
13497
13583
  try {
13498
13584
  const environment = ensureEnvironment(env);
13499
13585
  const client = await requireAuthenticatedClient();
@@ -13536,36 +13622,14 @@ var deleteCommand2 = new Command22("delete").description("Delete a project secre
13536
13622
  `Secret deleted successfully from ${environment}`
13537
13623
  );
13538
13624
  logger.newLine();
13539
- if (shouldPromptDeploy) {
13540
- const shouldRedeploy = await confirm14({
13541
- message: `Redeploy now to apply this change?`,
13542
- default: true
13543
- });
13544
- if (shouldRedeploy) {
13545
- await deployCommand.parseAsync(["--env", environment], {
13546
- from: "user"
13547
- });
13548
- } else {
13549
- logger.newLine();
13550
- logger.admonition("tip", "Next Steps", [
13551
- `Remember to deploy your project to apply the secret: \`playcademy deploy\``
13552
- ]);
13553
- logger.newLine();
13554
- }
13555
- } else {
13556
- logger.admonition("tip", "Next Steps", [
13557
- `Remember to deploy your project to apply the secret: \`playcademy deploy\``
13558
- ]);
13559
- logger.newLine();
13560
- }
13561
13625
  } catch (error) {
13562
13626
  logAndExit(error, { prefix: "Failed to delete secret" });
13563
13627
  }
13564
13628
  });
13565
13629
 
13566
13630
  // src/commands/secret/list.ts
13567
- import { Command as Command23 } from "commander";
13568
- var listCommand2 = new Command23("list").description("List project secret keys").option("--env <environment>", "Environment (staging or production)").action(async (options) => {
13631
+ import { Command as Command24 } from "commander";
13632
+ var listCommand2 = new Command24("list").description("List project secret keys").option("--env <environment>", "Environment (staging or production)").action(async (options) => {
13569
13633
  const { env } = options;
13570
13634
  try {
13571
13635
  const environment = ensureEnvironment(env);
@@ -13612,10 +13676,9 @@ var listCommand2 = new Command23("list").description("List project secret keys")
13612
13676
  });
13613
13677
 
13614
13678
  // src/commands/secret/set.ts
13615
- import { confirm as confirm15 } from "@inquirer/prompts";
13616
- import { Command as Command24 } from "commander";
13617
- var setCommand = new Command24("set").description("Set or update a project secret").argument("<key>", "Secret key (e.g., STRIPE_KEY)").argument("<value>", "Secret value").option("--env <environment>", "Environment (staging or production)").option("--no-deploy", "Skip deployment prompt").action(async (key, value, options) => {
13618
- const { env, deploy: shouldPromptDeploy = true } = options;
13679
+ import { Command as Command25 } from "commander";
13680
+ var setCommand = new Command25("set").description("Set or update a project secret").argument("<key>", "Secret key (e.g., STRIPE_KEY)").argument("<value>", "Secret value").option("--env <environment>", "Environment (staging or production)").action(async (key, value, options) => {
13681
+ const { env } = options;
13619
13682
  try {
13620
13683
  const environment = ensureEnvironment(env);
13621
13684
  const client = await requireAuthenticatedClient();
@@ -13643,42 +13706,20 @@ var setCommand = new Command24("set").description("Set or update a project secre
13643
13706
  await runStep(
13644
13707
  `Setting secret "${key}" in ${environment}`,
13645
13708
  () => client.dev.games.secrets.set(game.slug, { [key]: value }),
13646
- `Secret "${key}" set successfully in ${environment}`
13709
+ `Secret "${key}" applied to ${environment}`
13647
13710
  );
13648
13711
  logger.newLine();
13649
- if (shouldPromptDeploy) {
13650
- const shouldRedeploy = await confirm15({
13651
- message: `Redeploy now to apply this change?`,
13652
- default: true
13653
- });
13654
- if (shouldRedeploy) {
13655
- await deployCommand.parseAsync(["--env", environment], {
13656
- from: "user"
13657
- });
13658
- } else {
13659
- logger.newLine();
13660
- logger.admonition("tip", "Next Steps", [
13661
- `Remember to deploy your project to apply the secret: \`playcademy deploy\``
13662
- ]);
13663
- logger.newLine();
13664
- }
13665
- } else {
13666
- logger.admonition("tip", "Next Steps", [
13667
- `Remember to deploy your project to apply the secret: \`playcademy deploy\``
13668
- ]);
13669
- logger.newLine();
13670
- }
13671
13712
  } catch (error) {
13672
13713
  logAndExit(error, { prefix: "Failed to set secret" });
13673
13714
  }
13674
13715
  });
13675
13716
 
13676
13717
  // src/commands/secret/index.ts
13677
- var secretCommand = new Command25("secrets").description("Manage project secrets").addCommand(setCommand).addCommand(listCommand2).addCommand(deleteCommand2);
13718
+ var secretCommand = new Command26("secrets").description("Manage project secrets").addCommand(setCommand).addCommand(listCommand2).addCommand(deleteCommand2);
13678
13719
 
13679
13720
  // src/commands/types/index.ts
13680
- import { Command as Command26 } from "commander";
13681
- var typesCommand = new Command26("types").description("Generate TypeScript type definitions").action(async () => {
13721
+ import { Command as Command27 } from "commander";
13722
+ var typesCommand = new Command27("types").description("Generate TypeScript type definitions").action(async () => {
13682
13723
  try {
13683
13724
  logger.newLine();
13684
13725
  await runStep(
@@ -13693,10 +13734,10 @@ var typesCommand = new Command26("types").description("Generate TypeScript type
13693
13734
  });
13694
13735
 
13695
13736
  // src/commands/profiles/index.ts
13696
- import { Command as Command30 } from "commander";
13737
+ import { Command as Command31 } from "commander";
13697
13738
 
13698
13739
  // src/commands/profiles/list.ts
13699
- import { Command as Command27 } from "commander";
13740
+ import { Command as Command28 } from "commander";
13700
13741
  async function listProfilesAction() {
13701
13742
  try {
13702
13743
  const profilesMap = await listProfiles();
@@ -13738,12 +13779,12 @@ async function listProfilesAction() {
13738
13779
  process.exit(1);
13739
13780
  }
13740
13781
  }
13741
- var listCommand3 = new Command27("list").alias("ls").description("List all stored authentication profiles").action(listProfilesAction);
13782
+ var listCommand3 = new Command28("list").alias("ls").description("List all stored authentication profiles").action(listProfilesAction);
13742
13783
 
13743
13784
  // src/commands/profiles/remove.ts
13744
- import { bold as bold14 } from "colorette";
13745
- import { Command as Command28 } from "commander";
13746
- var removeCommand = new Command28("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) => {
13785
+ import { bold as bold15 } from "colorette";
13786
+ import { Command as Command29 } from "commander";
13787
+ var removeCommand = new Command29("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) => {
13747
13788
  const { env } = options;
13748
13789
  const environment = ensureEnvironment(env);
13749
13790
  try {
@@ -13757,7 +13798,7 @@ var removeCommand = new Command28("remove").alias("rm").description('Remove an a
13757
13798
  }
13758
13799
  await removeProfile(environment, name);
13759
13800
  logger.admonition("note", "Removed!", [
13760
- `Profile ${bold14(name)} removed from ${environment}`,
13801
+ `Profile ${bold15(name)} removed from ${environment}`,
13761
13802
  environment === "production" ? `To re-authenticate run \`playcademy login --env ${environment}\`` : "To re-authenticate run `playcademy login`"
13762
13803
  ]);
13763
13804
  logger.newLine();
@@ -13767,9 +13808,9 @@ var removeCommand = new Command28("remove").alias("rm").description('Remove an a
13767
13808
  });
13768
13809
 
13769
13810
  // src/commands/profiles/reset.ts
13770
- import { confirm as confirm16 } from "@inquirer/prompts";
13771
- import { Command as Command29 } from "commander";
13772
- var resetCommand = new Command29("reset").description(
13811
+ import { confirm as confirm15 } from "@inquirer/prompts";
13812
+ import { Command as Command30 } from "commander";
13813
+ var resetCommand = new Command30("reset").description(
13773
13814
  "Remove all authentication profiles across all environments (requires confirmation)"
13774
13815
  ).alias("clear").action(async () => {
13775
13816
  try {
@@ -13805,7 +13846,7 @@ var resetCommand = new Command29("reset").description(
13805
13846
  logger.newLine();
13806
13847
  }
13807
13848
  }
13808
- const confirmed = await confirm16({
13849
+ const confirmed = await confirm15({
13809
13850
  message: "Are you sure you want to remove all profiles?",
13810
13851
  default: false
13811
13852
  });
@@ -13845,18 +13886,18 @@ var resetCommand = new Command29("reset").description(
13845
13886
  });
13846
13887
 
13847
13888
  // src/commands/profiles/index.ts
13848
- var profilesCommand = new Command30("profiles").description("Manage authentication profiles").action(listProfilesAction);
13889
+ var profilesCommand = new Command31("profiles").description("Manage authentication profiles").action(listProfilesAction);
13849
13890
  profilesCommand.addCommand(listCommand3);
13850
13891
  profilesCommand.addCommand(removeCommand);
13851
13892
  profilesCommand.addCommand(resetCommand);
13852
13893
 
13853
13894
  // src/commands/timeback/index.ts
13854
- import { Command as Command36 } from "commander";
13895
+ import { Command as Command37 } from "commander";
13855
13896
 
13856
13897
  // src/commands/timeback/cleanup.ts
13857
- import { confirm as confirm17 } from "@inquirer/prompts";
13858
- import { Command as Command31 } from "commander";
13859
- var cleanupCommand = new Command31("cleanup").description("Remove TimeBack integration from your project").option(
13898
+ import { confirm as confirm16 } from "@inquirer/prompts";
13899
+ import { Command as Command32 } from "commander";
13900
+ var cleanupCommand = new Command32("cleanup").description("Remove TimeBack integration from your project").option(
13860
13901
  "--env <environment>",
13861
13902
  "Environment to remove TimeBack integration from (staging or production)"
13862
13903
  ).action(async (options) => {
@@ -13878,7 +13919,7 @@ var cleanupCommand = new Command31("cleanup").description("Remove TimeBack integ
13878
13919
  return;
13879
13920
  }
13880
13921
  displayCleanupWarning(integrations, game.displayName, logger);
13881
- const confirmed = await confirm17({
13922
+ const confirmed = await confirm16({
13882
13923
  message: "Are you sure you want to remove TimeBack integration?",
13883
13924
  default: false
13884
13925
  });
@@ -13908,8 +13949,8 @@ var cleanupCommand = new Command31("cleanup").description("Remove TimeBack integ
13908
13949
  });
13909
13950
 
13910
13951
  // src/commands/timeback/init.ts
13911
- import { Command as Command32 } from "commander";
13912
- var initCommand2 = new Command32("init").description("Add TimeBack integration to your project").action(async () => {
13952
+ import { Command as Command33 } from "commander";
13953
+ var initCommand2 = new Command33("init").description("Add TimeBack integration to your project").action(async () => {
13913
13954
  try {
13914
13955
  logger.newLine();
13915
13956
  const configPath = await requireConfigFile();
@@ -13958,8 +13999,8 @@ var initCommand2 = new Command32("init").description("Add TimeBack integration t
13958
13999
  });
13959
14000
 
13960
14001
  // src/commands/timeback/setup.ts
13961
- import { Command as Command33 } from "commander";
13962
- var setupCommand = new Command33("setup").description("Set up TimeBack integration for your project").option("--dry-run", "Validate configuration without creating resources").option("--verbose, -v", "Output detailed information").option(
14002
+ import { Command as Command34 } from "commander";
14003
+ var setupCommand = new Command34("setup").description("Set up TimeBack integration for your project").option("--dry-run", "Validate configuration without creating resources").option("--verbose, -v", "Output detailed information").option(
13963
14004
  "--env <environment>",
13964
14005
  "Environment to set up TimeBack integration in (staging or production)"
13965
14006
  ).action(async (options) => {
@@ -14043,8 +14084,8 @@ var setupCommand = new Command33("setup").description("Set up TimeBack integrati
14043
14084
  });
14044
14085
 
14045
14086
  // src/commands/timeback/update.ts
14046
- import { Command as Command34 } from "commander";
14047
- var updateCommand2 = new Command34("update").description("Update TimeBack integration configuration for your project").option("--verbose, -v", "Output detailed information").option(
14087
+ import { Command as Command35 } from "commander";
14088
+ var updateCommand2 = new Command35("update").description("Update TimeBack integration configuration for your project").option("--verbose, -v", "Output detailed information").option(
14048
14089
  "--env <environment>",
14049
14090
  "Environment to update TimeBack integration in (staging or production)"
14050
14091
  ).action(async (options) => {
@@ -14104,8 +14145,8 @@ var updateCommand2 = new Command34("update").description("Update TimeBack integr
14104
14145
  });
14105
14146
 
14106
14147
  // src/commands/timeback/verify.ts
14107
- import { Command as Command35 } from "commander";
14108
- var verifyCommand = new Command35("verify").description("Verify TimeBack integration for your project").option("--verbose, -v", "Output detailed resource information").option(
14148
+ import { Command as Command36 } from "commander";
14149
+ var verifyCommand = new Command36("verify").description("Verify TimeBack integration for your project").option("--verbose, -v", "Output detailed resource information").option(
14109
14150
  "--env <environment>",
14110
14151
  "Environment to verify TimeBack integration in (staging or production)"
14111
14152
  ).action(async (options) => {
@@ -14150,7 +14191,7 @@ var verifyCommand = new Command35("verify").description("Verify TimeBack integra
14150
14191
  });
14151
14192
 
14152
14193
  // src/commands/timeback/index.ts
14153
- var timebackCommand = new Command36("timeback").description(
14194
+ var timebackCommand = new Command37("timeback").description(
14154
14195
  "TimeBack integration management"
14155
14196
  );
14156
14197
  timebackCommand.addCommand(initCommand2);
@@ -14160,13 +14201,13 @@ timebackCommand.addCommand(verifyCommand);
14160
14201
  timebackCommand.addCommand(cleanupCommand);
14161
14202
 
14162
14203
  // src/commands/debug/index.ts
14163
- import { Command as Command38 } from "commander";
14204
+ import { Command as Command39 } from "commander";
14164
14205
 
14165
14206
  // src/commands/debug/bundle.ts
14166
14207
  import { writeFileSync as writeFileSync16 } from "fs";
14167
14208
  import { join as join51 } from "path";
14168
- import { Command as Command37 } from "commander";
14169
- var bundleCommand = new Command37("bundle").description("Bundle and inspect the project backend worker code (for debugging)").option("-o, --output <path>", "Output file path", CLI_DEFAULT_OUTPUTS.WORKER_BUNDLE).option("--minify", "Minify the output").option("--sourcemap", "Include source maps").action(async (options) => {
14209
+ import { Command as Command38 } from "commander";
14210
+ var bundleCommand = new Command38("bundle").description("Bundle and inspect the project backend worker code (for debugging)").option("-o, --output <path>", "Output file path", CLI_DEFAULT_OUTPUTS.WORKER_BUNDLE).option("--minify", "Minify the output").option("--sourcemap", "Include source maps").action(async (options) => {
14170
14211
  try {
14171
14212
  const workspace = getWorkspace();
14172
14213
  logger.newLine();
@@ -14235,15 +14276,15 @@ var bundleCommand = new Command37("bundle").description("Bundle and inspect the
14235
14276
  });
14236
14277
 
14237
14278
  // src/commands/debug/index.ts
14238
- var debugCommand = new Command38("debug").description("Debug and inspect project builds").addCommand(bundleCommand);
14279
+ var debugCommand = new Command39("debug").description("Debug and inspect project builds").addCommand(bundleCommand);
14239
14280
 
14240
14281
  // src/commands/vite/index.ts
14241
- import { Command as Command39 } from "commander";
14282
+ import { Command as Command40 } from "commander";
14242
14283
 
14243
14284
  // src/commands/vite/config.ts
14244
14285
  import path3 from "node:path";
14245
- import { confirm as confirm18 } from "@inquirer/prompts";
14246
- import { dim as dim11 } from "colorette";
14286
+ import { confirm as confirm17 } from "@inquirer/prompts";
14287
+ import { dim as dim12 } from "colorette";
14247
14288
  async function runViteConfig() {
14248
14289
  try {
14249
14290
  logger.newLine();
@@ -14252,7 +14293,7 @@ async function runViteConfig() {
14252
14293
  if (!existingViteConfig) {
14253
14294
  logger.warn("No vite config file found in your project");
14254
14295
  logger.newLine();
14255
- const shouldCreate = await confirm18({
14296
+ const shouldCreate = await confirm17({
14256
14297
  message: "Would you like to create a new vite.config.ts?",
14257
14298
  default: true
14258
14299
  });
@@ -14285,7 +14326,7 @@ async function runViteConfig() {
14285
14326
  const devCommand2 = getRunCommand(pm, "dev");
14286
14327
  const buildCommand = getRunCommand(pm, "build");
14287
14328
  logger.admonition("tip", "Next Steps", [
14288
- `1. Configure options in <vite.config.ts> ${dim11("(optional)")}`,
14329
+ `1. Configure options in <vite.config.ts> ${dim12("(optional)")}`,
14289
14330
  `2. Run \`${devCommand2}\` to start development`,
14290
14331
  `3. Run \`${buildCommand}\` to build your project`,
14291
14332
  "",
@@ -14302,7 +14343,7 @@ async function runViteConfig() {
14302
14343
  }
14303
14344
 
14304
14345
  // src/commands/vite/index.ts
14305
- var viteCommand = new Command39("vite").description("Vite plugin management");
14346
+ var viteCommand = new Command40("vite").description("Vite plugin management");
14306
14347
  viteCommand.command("config").description("Add Vite plugin to your project").action(runViteConfig);
14307
14348
 
14308
14349
  // src/index.ts
@@ -14332,6 +14373,7 @@ program.addCommand(typesCommand);
14332
14373
  program.addCommand(viteCommand);
14333
14374
  program.addCommand(projectsCommand);
14334
14375
  program.addCommand(deployCommand);
14376
+ program.addCommand(logsCommand);
14335
14377
  program.addCommand(timebackCommand);
14336
14378
  program.addCommand(debugCommand);
14337
14379
  program.addCommand(updateCommand);
@@ -14350,6 +14392,7 @@ export {
14350
14392
  analyzeChanges,
14351
14393
  applyEngineSelection,
14352
14394
  buildCustomRouteImportStatements,
14395
+ buildLogStreamUrl,
14353
14396
  buildStatusByCourseId,
14354
14397
  bundleBackend,
14355
14398
  bundleSeedWorker,
@@ -14360,6 +14403,7 @@ export {
14360
14403
  collectFiles,
14361
14404
  compareIntegrationKeys,
14362
14405
  confirmDeploymentPlan,
14406
+ connectToLogStream,
14363
14407
  createClient,
14364
14408
  createProjectDirectory,
14365
14409
  createSeedWorkerEntry,
@@ -14377,6 +14421,7 @@ export {
14377
14421
  displayDomainValidationRecords,
14378
14422
  displayIntegrationDetails,
14379
14423
  displayIntegrationList,
14424
+ displayLogEntry,
14380
14425
  displayMissingCourseRequirementsAndExit,
14381
14426
  displayResourcesStatus,
14382
14427
  displaySeedLogs,