playcademy 0.28.1-beta.9 → 0.29.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/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # playcademy
2
2
 
3
- The official command-line interface for Playcademy - deploy and share your educational games with ease.
3
+ **The official command-line interface for deploying and managing Playcademy games.**
4
+
5
+ ## Overview
6
+
7
+ The `playcademy` CLI authenticates developers, configures games, deploys frontend and backend
8
+ resources, and manages integrations such as TimeBack.
4
9
 
5
10
  ## Installation
6
11
 
@@ -186,7 +191,7 @@ my-game/
186
191
 
187
192
  Backend routes are automatically deployed with hosted games.
188
193
 
189
- ## Authentication
194
+ ### Credential Storage
190
195
 
191
196
  Credentials are stored in `~/.playcademy/auth.json`:
192
197
 
@@ -277,7 +282,22 @@ playcademy timeback assessments import --env production
277
282
  Fixtures are written to `.playcademy/timeback/assessment-fixtures.json`. The local dev host serves
278
283
  them through the normal game SDK assessment methods and resets local attempts when it restarts.
279
284
 
280
- ## Environment Variables
285
+ #### `timeback assessments assets upload`
286
+
287
+ Upload one public assessment image to immutable, content-addressed CDN storage. Production is the
288
+ default for this command; pass `--env staging` explicitly when testing the upload flow.
289
+
290
+ ```bash
291
+ playcademy timeback assessments assets upload ./diagram.png
292
+ playcademy timeback assessments assets upload ./diagram.png --env staging
293
+ playcademy timeback assessments assets upload ./diagram.png --json
294
+ ```
295
+
296
+ Uploads accept PNG, JPEG, single-frame GIF, single-frame WebP, and uncompressed BMP files up to
297
+ 4 MiB and 25 megapixels. Re-uploading identical bytes returns the existing URL without creating
298
+ another object.
299
+
300
+ ### Environment Variables
281
301
 
282
302
  - `CI` - Set to `1` or `true` to disable interactive prompts
283
303
  - `PLAYCADEMY_API_TOKEN` - API token for non-interactive authentication
@@ -300,7 +320,7 @@ PLAYCADEMY_PROFILE=work playcademy deploy
300
320
  PLAYCADEMY_BASE_URL=http://localhost:5174 playcademy deploy
301
321
  ```
302
322
 
303
- ## CI/CD
323
+ ### CI/CD
304
324
 
305
325
  Use `CI=1`, `PLAYCADEMY_API_TOKEN`, and an explicit environment for pipeline deploys:
306
326
 
@@ -361,7 +381,7 @@ jobs:
361
381
  PLAYCADEMY_API_TOKEN: ${{ secrets.PLAYCADEMY_API_TOKEN }}
362
382
  ```
363
383
 
364
- ## Configuration Files
384
+ ### Configuration Files
365
385
 
366
386
  The CLI supports multiple configuration formats:
367
387
 
@@ -371,7 +391,18 @@ The CLI supports multiple configuration formats:
371
391
 
372
392
  Priority: CLI flags > config file > interactive prompts
373
393
 
374
- ## Architecture
394
+ ## Development
395
+
396
+ From the repository root, install dependencies and run the CLI directly from source:
397
+
398
+ ```bash
399
+ bun install
400
+ bun packages/cli/src/main.ts --help
401
+ ```
402
+
403
+ Run `bun run check` and the relevant targeted tests before submitting changes.
404
+
405
+ ## Dependencies
375
406
 
376
407
  Built with:
377
408
 
@@ -380,6 +411,25 @@ Built with:
380
411
  - **Colorette** - Terminal styling
381
412
  - **esbuild** - Bundling (for encoding compatibility)
382
413
 
414
+ ## Examples
415
+
416
+ ```bash
417
+ # Inspect the authenticated account
418
+ playcademy me
419
+
420
+ # Validate a staging deployment without publishing it
421
+ playcademy deploy --env staging --dry-run
422
+
423
+ # Upload an immutable assessment image to staging
424
+ playcademy timeback assessments assets upload ./diagram.png --env staging
425
+ ```
426
+
427
+ ## Related Packages
428
+
429
+ - [`@playcademy/sdk`](../sdk) - Runtime and server APIs used by Playcademy games
430
+ - [`@playcademy/vite-plugin`](../vite-plugin) - Vite integration for Playcademy projects
431
+ - [`@playcademy/timeback`](../timeback) - Shared TimeBack integration utilities
432
+
383
433
  ## Contributing
384
434
 
385
435
  See the main [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines.
package/dist/bin.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
2
3
 
3
4
  // src/bin.ts
4
5
  import { spawnSync } from "node:child_process";
package/dist/bucket.d.ts CHANGED
@@ -138,6 +138,19 @@ interface DashboardThemeConfig {
138
138
  /** Secondary/accent color as a hex string, e.g. '#ffd166' */
139
139
  secondary?: string;
140
140
  }
141
+ /**
142
+ * Child-catalog contract generation (see @playcademy/sdk/contracts).
143
+ * The extractor is a repo-local script (run with the CLI's runtime) that
144
+ * prints `{ generatedFrom, deliveries }` as JSON on stdout; the CLI
145
+ * validates, stamps the contract fields, and writes
146
+ * `.playcademy/catalog.json` deterministically. The document carries no
147
+ * app identity — consuming repos assign the namespace key from sync
148
+ * provenance.
149
+ */
150
+ interface CatalogConfig {
151
+ /** Path to the extractor script, relative to the config file. */
152
+ extractor: string;
153
+ }
141
154
  /**
142
155
  * Unified Playcademy configuration
143
156
  * Used for playcademy.config.{js,json}
@@ -170,6 +183,8 @@ interface PlaycademyConfig {
170
183
  dashboard?: DashboardConfig | boolean;
171
184
  /** Integrations (database, custom routes, external services) */
172
185
  integrations?: IntegrationsConfig;
186
+ /** Child-catalog contract generation (`playcademy catalog`) */
187
+ catalog?: CatalogConfig;
173
188
  }
174
189
 
175
190
  /**
package/dist/bucket.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
1
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
2
3
  var __esm = (fn, res) => function __init() {
3
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -20,7 +21,7 @@ var init_file_loader = __esm({
20
21
 
21
22
  // src/lib/bucket/dev.ts
22
23
  import { existsSync } from "fs";
23
- import { join as join8 } from "path";
24
+ import { join as join7 } from "path";
24
25
 
25
26
  // ../edge-play/src/lib/constants.ts
26
27
  var ASSET_PREFIX = "_assets/";
@@ -28,10 +29,7 @@ var MANIFEST_KEY = "_manifest.json";
28
29
  var ASSET_ROUTE_PREFIX = "/api/assets/";
29
30
 
30
31
  // src/lib/bundle/game-backend.ts
31
- import { join as join6 } from "node:path";
32
-
33
- // src/constants/api.ts
34
- import { join as join2 } from "node:path";
32
+ import { join as join5 } from "node:path";
35
33
 
36
34
  // src/constants/server.ts
37
35
  import { join } from "node:path";
@@ -39,12 +37,12 @@ var GAME_SERVER_ROOT_DIRECTORY = "server";
39
37
  var GAME_SERVER_LIB_DIRECTORY = join(GAME_SERVER_ROOT_DIRECTORY, "lib");
40
38
 
41
39
  // src/constants/api.ts
42
- var GAME_API_ROUTES_DIRECTORY = join2(GAME_SERVER_ROOT_DIRECTORY, "api");
40
+ var GAME_API_ROUTES_DIRECTORY = `${GAME_SERVER_ROOT_DIRECTORY}/api`;
43
41
 
44
42
  // ../better-auth/package.json
45
43
  var package_default = {
46
44
  name: "@playcademy/better-auth",
47
- version: "0.0.21-beta.9",
45
+ version: "0.0.21",
48
46
  type: "module",
49
47
  exports: {
50
48
  "./server": {
@@ -87,18 +85,7 @@ var package_default = {
87
85
  // src/constants/auth.ts
88
86
  var BETTER_AUTH_VERSION = package_default.dependencies["better-auth"];
89
87
 
90
- // src/constants/config.ts
91
- var ENV_FILES = [
92
- ".env",
93
- // Loaded first
94
- ".env.development",
95
- // Overrides .env
96
- ".env.local"
97
- // Overrides all (highest priority)
98
- ];
99
-
100
88
  // src/constants/bucket.ts
101
- var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
102
89
  var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
103
90
 
104
91
  // src/constants/cloudflare.ts
@@ -112,45 +99,48 @@ var CLOUDFLARE_BINDINGS = {
112
99
  };
113
100
 
114
101
  // src/constants/database.ts
115
- import { join as join3 } from "path";
116
- var DEFAULT_DATABASE_DIRECTORY = join3("server", "db");
102
+ import { join as join2 } from "path";
103
+ var DEFAULT_DATABASE_DIRECTORY = join2("server", "db");
117
104
 
118
105
  // src/constants/godot.ts
119
- import { join as join4 } from "node:path";
106
+ import { join as join3 } from "node:path";
120
107
  var GODOT_BUILD_DIRECTORIES = {
121
108
  /** Root build directory (cleared before each export) */
122
109
  ROOT: "build",
123
110
  /** Web export subdirectory */
124
- WEB: join4("build", "web")
111
+ WEB: join3("build", "web")
125
112
  };
126
113
  var GODOT_BUILD_OUTPUTS = {
127
114
  /** Exported web build entry point */
128
- INDEX_HTML: join4("build", "web", "index.html"),
115
+ INDEX_HTML: join3("build", "web", "index.html"),
129
116
  /** Packaged zip file (created by Godot export) */
130
- ZIP: join4("build", "web_playcademy.zip")
117
+ ZIP: join3("build", "web_playcademy.zip")
131
118
  };
132
119
 
133
120
  // src/constants/paths.ts
134
121
  import { homedir } from "node:os";
135
- import { join as join5 } from "node:path";
122
+ import { join as join4 } from "node:path";
136
123
  var WORKSPACE_NAME = ".playcademy";
137
124
  var CLI_DIRECTORIES = {
138
125
  WORKSPACE: WORKSPACE_NAME,
139
- DATABASE: join5(WORKSPACE_NAME, "db"),
140
- KV: join5(WORKSPACE_NAME, "kv"),
141
- BUCKET: join5(WORKSPACE_NAME, "bucket"),
142
- TIMEBACK: join5(WORKSPACE_NAME, "timeback")
126
+ DATABASE: join4(WORKSPACE_NAME, "db"),
127
+ KV: join4(WORKSPACE_NAME, "kv"),
128
+ BUCKET: join4(WORKSPACE_NAME, "bucket"),
129
+ TIMEBACK: join4(WORKSPACE_NAME, "timeback")
143
130
  };
144
- var PLAYCADEMY_HOME = join5(homedir(), ".playcademy");
131
+ var PLAYCADEMY_HOME = join4(homedir(), ".playcademy");
145
132
  var CLI_USER_DIRECTORIES = {
146
133
  CONFIG: PLAYCADEMY_HOME,
147
- BIN: join5(PLAYCADEMY_HOME, "bin"),
148
- CACHE: join5(PLAYCADEMY_HOME, "cache")
134
+ BIN: join4(PLAYCADEMY_HOME, "bin"),
135
+ CACHE: join4(PLAYCADEMY_HOME, "cache")
149
136
  };
150
137
  var CLI_DEFAULT_OUTPUTS = {
151
- WORKER_BUNDLE: join5(WORKSPACE_NAME, "worker-bundle.js")
138
+ WORKER_BUNDLE: join4(WORKSPACE_NAME, "worker-bundle.js")
152
139
  };
153
140
 
141
+ // ../constants/src/platform.ts
142
+ var ASSESSMENT_ASSET_MAX_BYTES = 4 * 1024 * 1024;
143
+
154
144
  // ../constants/src/timeback.ts
155
145
  var TIMEBACK_GAME_METRIC_DECIMAL_PLACES = {
156
146
  xp: 1,
@@ -344,14 +334,14 @@ function resolveGameRoutesDir(config) {
344
334
  return typeof customRoutes === "object" && customRoutes.directory || GAME_API_ROUTES_DIRECTORY;
345
335
  }
346
336
  function getGameRoutesDirectory(projectPath, config) {
347
- return join6(projectPath, resolveGameRoutesDir(config));
337
+ return join5(projectPath, resolveGameRoutesDir(config));
348
338
  }
349
339
 
350
340
  // src/lib/bucket/local.ts
351
- import { join as join7 } from "path";
341
+ import { join as join6 } from "path";
352
342
  import { Miniflare } from "miniflare";
353
343
  async function openLocalBucket() {
354
- const bucketDir = join7(getWorkspace(), CLI_DIRECTORIES.BUCKET);
344
+ const bucketDir = join6(getWorkspace(), CLI_DIRECTORIES.BUCKET);
355
345
  const mf = new Miniflare({
356
346
  modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
357
347
  r2Buckets: [CLOUDFLARE_BINDINGS.BUCKET],
@@ -368,7 +358,7 @@ async function openLocalBucket() {
368
358
  var ASSET_DEV_ROUTE_PREFIX = ASSET_ROUTE_PREFIX;
369
359
  function hasCustomAssetsRoute(projectRoot, config) {
370
360
  const routesDir = getGameRoutesDirectory(projectRoot, config);
371
- return existsSync(join8(routesDir, "assets")) || existsSync(join8(routesDir, "assets.ts"));
361
+ return existsSync(join7(routesDir, "assets")) || existsSync(join7(routesDir, "assets.ts"));
372
362
  }
373
363
  function openLocalAssetBucket() {
374
364
  return openLocalBucket();