star-sdk-cli 0.1.10 → 0.1.12

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +171 -25
  2. package/package.json +15 -1
package/dist/cli.mjs CHANGED
@@ -23,6 +23,23 @@ import Star from 'star-sdk';
23
23
 
24
24
  **CRITICAL:** Always use \`import Star from 'star-sdk'\` - not destructured imports.
25
25
 
26
+ ## Setup (Required for Leaderboards)
27
+
28
+ Before writing any leaderboard code, register your game by running this command in the terminal:
29
+
30
+ \`\`\`bash
31
+ npx star-sdk init "Your Game Name"
32
+ \`\`\`
33
+
34
+ This creates a \`.starrc\` file containing your gameId. Open \`.starrc\`, copy the \`gameId\` value, and pass it to \`Star.init()\`:
35
+
36
+ \`\`\`javascript
37
+ import Star from 'star-sdk';
38
+ Star.init({ gameId: '<paste gameId from .starrc>' });
39
+ \`\`\`
40
+
41
+ **The gameId is a unique token issued by the Star API. Do not make one up \u2014 it will not work. You must run \`npx star-sdk init\` first and use the gameId it gives you.**
42
+
26
43
  ## API Overview
27
44
 
28
45
  | API | Use When | Docs |
@@ -35,7 +52,7 @@ import Star from 'star-sdk';
35
52
 
36
53
  \`\`\`javascript
37
54
  import Star from 'star-sdk';
38
- Star.init({ gameId: 'YOUR_GAME_ID' }); // from .starrc
55
+ Star.init({ gameId: '<gameId from .starrc>' }); // run: npx star-sdk init
39
56
 
40
57
  Star.game(ctx => {
41
58
  const { canvas, width, height, ctx: c } = ctx;
@@ -72,6 +89,20 @@ function gameOver(finalScore) {
72
89
  }
73
90
  \`\`\`
74
91
 
92
+ ### Lower-is-Better Games (Time, Moves, Golf)
93
+
94
+ For games where lower scores win, set \`sort: 'asc'\` in \`Star.init()\`:
95
+
96
+ \`\`\`javascript
97
+ Star.init({ gameId: '<gameId from .starrc>', leaderboard: { sort: 'asc' } });
98
+
99
+ // Submit the raw value \u2014 do NOT invert the score
100
+ Star.leaderboard.submit(reactionTimeMs);
101
+ Star.leaderboard.show();
102
+ \`\`\`
103
+
104
+ **Do NOT** invert scores to fake ascending order (e.g., \`10000 - score\`). Use \`sort: 'asc'\` instead.
105
+
75
106
  ### Audio (It Just Works)
76
107
 
77
108
  Star.audio handles mobile audio unlocking automatically. Just call \`play()\` - no special handling needed.
@@ -109,7 +140,7 @@ Only these 17 presets exist:
109
140
 
110
141
  \`\`\`javascript
111
142
  import Star from 'star-sdk';
112
- Star.init({ gameId: 'YOUR_GAME_ID' }); // from .starrc
143
+ Star.init({ gameId: '<gameId from .starrc>' }); // run: npx star-sdk init
113
144
 
114
145
  Star.game(ctx => {
115
146
  const { canvas, width, height, ctx: c } = ctx;
@@ -190,6 +221,16 @@ Star.game(ctx => {
190
221
  \`\`\`
191
222
 
192
223
  For detailed API documentation, see the linked files above.
224
+
225
+ ## Examples
226
+
227
+ Complete working games are in the \`examples/\` directory (also published with the npm package):
228
+
229
+ - **click-frenzy** \u2014 5-second click speed game with leaderboard
230
+ - **dodge** \u2014 Avoid falling obstacles, keyboard and touch controls
231
+ - **reaction-time** \u2014 Test your reflexes over 5 rounds, DOM-based (no canvas)
232
+
233
+ Each example is a single HTML file, no build step \u2014 imports from esm.sh.
193
234
  `;
194
235
  var AUDIO_DOCS = `**Installation**
195
236
 
@@ -665,7 +706,7 @@ game(({ ctx, width, height, loop, toStagePoint, canvas }) => {
665
706
  import { game } from 'star-canvas';
666
707
  import { createLeaderboard } from 'star-leaderboard';
667
708
 
668
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
709
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
669
710
 
670
711
  game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
671
712
  let score = 0;
@@ -1006,6 +1047,16 @@ var LEADERBOARD_DOCS = `**Installation**
1006
1047
  yarn add star-leaderboard
1007
1048
  \`\`\`
1008
1049
 
1050
+ **Setup (do this first):**
1051
+
1052
+ Register your game by running this in the terminal:
1053
+
1054
+ \`\`\`bash
1055
+ npx star-sdk init "Game Name"
1056
+ \`\`\`
1057
+
1058
+ This creates a \`.starrc\` file. Open it, copy the \`gameId\` value, and use it below. The gameId is a server-issued token \u2014 do not invent one. It must come from \`npx star-sdk init\`.
1059
+
1009
1060
  ### Star Leaderboard SDK
1010
1061
 
1011
1062
  **Simple leaderboards for Star games.** Submit scores, show rankings, share results. Never crashes your game.
@@ -1013,7 +1064,7 @@ yarn add star-leaderboard
1013
1064
  **Import:**
1014
1065
  \`\`\`javascript
1015
1066
  import { createLeaderboard } from 'star-leaderboard';
1016
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1067
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1017
1068
  \`\`\`
1018
1069
 
1019
1070
  **CRITICAL:** Import in JavaScript - don't add \`<script>\` tags.
@@ -1026,7 +1077,7 @@ const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1026
1077
  import { createLeaderboard } from 'star-leaderboard';
1027
1078
  import { game } from 'star-canvas';
1028
1079
 
1029
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1080
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1030
1081
 
1031
1082
  game(({ ctx, width, height, loop, ui, on, canvas }) => {
1032
1083
  let score = 0;
@@ -1082,7 +1133,7 @@ leaderboard.showLeaderboard() // Same as show()
1082
1133
  \`\`\`javascript
1083
1134
  import { createLeaderboard } from 'star-leaderboard';
1084
1135
 
1085
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1136
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1086
1137
 
1087
1138
  function gameOver(finalScore) {
1088
1139
  // Fire and forget - simplest approach
@@ -1096,7 +1147,7 @@ function gameOver(finalScore) {
1096
1147
  \`\`\`javascript
1097
1148
  import { createLeaderboard } from 'star-leaderboard';
1098
1149
 
1099
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1150
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1100
1151
 
1101
1152
  async function gameOver(finalScore) {
1102
1153
  const { success, rank } = await leaderboard.submit(finalScore);
@@ -1115,7 +1166,7 @@ async function gameOver(finalScore) {
1115
1166
  import { createLeaderboard } from 'star-leaderboard';
1116
1167
  import { game } from 'star-canvas';
1117
1168
 
1118
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1169
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1119
1170
 
1120
1171
  game(({ ui, on }) => {
1121
1172
  ui.render(\`
@@ -1136,7 +1187,7 @@ game(({ ui, on }) => {
1136
1187
  \`\`\`javascript
1137
1188
  import { createLeaderboard } from 'star-leaderboard';
1138
1189
 
1139
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1190
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1140
1191
 
1141
1192
  async function showCustomLeaderboard() {
1142
1193
  const { scores, you, config } = await leaderboard.getScores({
@@ -1161,7 +1212,7 @@ async function showCustomLeaderboard() {
1161
1212
  import { createLeaderboard } from 'star-leaderboard';
1162
1213
  import { game } from 'star-canvas';
1163
1214
 
1164
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1215
+ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
1165
1216
 
1166
1217
  game(({ ctx, width, height, loop, ui, on, canvas }) => {
1167
1218
  let score = 0;
@@ -1214,11 +1265,7 @@ game(({ ctx, width, height, loop, ui, on, canvas }) => {
1214
1265
 
1215
1266
  **Configuration:**
1216
1267
 
1217
- Always pass your gameId (from \`.starrc\`, created by \`npx star-sdk init\`):
1218
- \`\`\`javascript
1219
- import { createLeaderboard } from 'star-leaderboard';
1220
- const leaderboard = createLeaderboard({ gameId: 'YOUR_GAME_ID' });
1221
- \`\`\`
1268
+ Your gameId comes from \`.starrc\` (created by \`npx star-sdk init\`). See Setup at the top.
1222
1269
 
1223
1270
  ---
1224
1271
 
@@ -1286,6 +1333,7 @@ ${colors.bright}Star SDK CLI${colors.reset} v${VERSION}
1286
1333
 
1287
1334
  ${colors.dim}Commands:${colors.reset}
1288
1335
  ${colors.cyan}init <name>${colors.reset} Register a new game
1336
+ ${colors.cyan}deploy [path]${colors.reset} Deploy game to Star hosting
1289
1337
  ${colors.cyan}install [agent]${colors.reset} Install Star SDK docs for AI coding agents
1290
1338
  ${colors.cyan}docs [topic]${colors.reset} Print API documentation (default: all docs)
1291
1339
  ${colors.cyan}whoami${colors.reset} Show current configuration
@@ -1308,6 +1356,10 @@ ${colors.dim}Examples:${colors.reset}
1308
1356
  ${colors.dim}# Register a new game${colors.reset}
1309
1357
  npx star-sdk init "My Awesome Game"
1310
1358
 
1359
+ ${colors.dim}# Deploy to Star hosting${colors.reset}
1360
+ npx star-sdk deploy ${colors.dim}# Deploy current directory${colors.reset}
1361
+ npx star-sdk deploy ./dist ${colors.dim}# Deploy specific directory${colors.reset}
1362
+
1311
1363
  ${colors.dim}# Install for different AI agents${colors.reset}
1312
1364
  npx star-sdk install ${colors.dim}# Claude Code (default)${colors.reset}
1313
1365
  npx star-sdk install codex ${colors.dim}# OpenAI Codex${colors.reset}
@@ -1320,7 +1372,7 @@ ${colors.dim}Examples:${colors.reset}
1320
1372
  npx star-sdk docs audio ${colors.dim}# Just audio API${colors.reset}
1321
1373
  npx star-sdk docs skill ${colors.dim}# Just main skill file${colors.reset}
1322
1374
 
1323
- ${colors.dim}Learn more:${colors.reset} https://buildwithstar.com/docs
1375
+ ${colors.dim}Learn more:${colors.reset} https://buildwithstar.com/docs/sdk
1324
1376
  `);
1325
1377
  }
1326
1378
  function showVersion() {
@@ -1380,7 +1432,8 @@ async function initCommand(name, email) {
1380
1432
  gameId: data.gameId,
1381
1433
  name: data.name,
1382
1434
  email: email?.trim(),
1383
- dashboardUrl: data.dashboardUrl
1435
+ dashboardUrl: data.dashboardUrl,
1436
+ deployKey: data.deployKey
1384
1437
  };
1385
1438
  saveConfig(config);
1386
1439
  log("");
@@ -1392,20 +1445,20 @@ async function initCommand(name, email) {
1392
1445
  log(`${colors.dim}Config saved to${colors.reset} ${colors.bright}.starrc${colors.reset}`);
1393
1446
  log("");
1394
1447
  log(`${colors.dim}Next steps:${colors.reset}`);
1395
- log(` 1. Install the leaderboard package: ${colors.cyan}yarn add star-leaderboard${colors.reset}`);
1396
- log(` 2. Submit scores and show the leaderboard`);
1448
+ log(` 1. Add the SDK: ${colors.cyan}npm install star-sdk${colors.reset}`);
1449
+ log(` 2. Build your game, then deploy: ${colors.cyan}npx star-sdk deploy${colors.reset}`);
1397
1450
  log("");
1398
1451
  log(`${colors.dim}Example:${colors.reset}`);
1399
- log(` ${colors.cyan}import { createLeaderboard } from 'star-leaderboard';${colors.reset}`);
1400
- log(` ${colors.cyan}const leaderboard = createLeaderboard({ gameId: '${data.gameId}' });${colors.reset}`);
1401
- log(` ${colors.cyan}leaderboard.submit(1500);${colors.reset}`);
1402
- log(` ${colors.cyan}leaderboard.show();${colors.reset}`);
1452
+ log(` ${colors.cyan}import Star from 'star-sdk';${colors.reset}`);
1453
+ log(` ${colors.cyan}Star.init({ gameId: '${data.gameId}' });${colors.reset}`);
1454
+ log(` ${colors.cyan}Star.leaderboard.submit(1500);${colors.reset}`);
1455
+ log(` ${colors.cyan}Star.leaderboard.show();${colors.reset}`);
1403
1456
  log("");
1404
1457
  log(`${colors.dim}Using an AI coding agent?${colors.reset}`);
1405
- log(` ${colors.cyan}npx star-sdk install${colors.reset} ${colors.dim}Claude Code, Codex, Cursor, Windsurf, Aider${colors.reset}`);
1458
+ log(` ${colors.cyan}npx star-sdk install${colors.reset} ${colors.dim}Claude Code, Codex, Cursor, Aider${colors.reset}`);
1406
1459
  log("");
1407
1460
  log(`${colors.dim}Documentation:${colors.reset}`);
1408
- log(` ${colors.cyan}https://buildwithstar.com/docs${colors.reset}`);
1461
+ log(` ${colors.cyan}https://buildwithstar.com/docs/sdk/sdk${colors.reset}`);
1409
1462
  log("");
1410
1463
  } catch (err) {
1411
1464
  if (err.code === "ECONNREFUSED" || err.code === "ENOTFOUND") {
@@ -1435,8 +1488,98 @@ function whoamiCommand() {
1435
1488
  if (config.dashboardUrl) {
1436
1489
  log(` ${colors.dim}Dashboard:${colors.reset} ${colors.cyan}${config.dashboardUrl}${colors.reset}`);
1437
1490
  }
1491
+ if (config.deployUrl) {
1492
+ log(` ${colors.dim}Live URL:${colors.reset} ${colors.cyan}${config.deployUrl}${colors.reset}`);
1493
+ }
1494
+ if (config.deployKey) {
1495
+ log(` ${colors.dim}Deploy key:${colors.reset} ${colors.dim}${config.deployKey.slice(0, 8)}...${colors.reset}`);
1496
+ }
1438
1497
  log("");
1439
1498
  }
1499
+ async function deployCommand(dirPath) {
1500
+ const config = loadConfig();
1501
+ if (!config) {
1502
+ error('No .starrc found. Run "npx star-sdk init <name>" first.');
1503
+ process.exit(1);
1504
+ }
1505
+ if (!config.deployKey) {
1506
+ error('No deploy key found. Re-run "npx star-sdk init <name>" to get one.');
1507
+ process.exit(1);
1508
+ }
1509
+ const deployDir = dirPath ? path.resolve(dirPath) : process.cwd();
1510
+ if (!fs.existsSync(deployDir)) {
1511
+ error(`Directory not found: ${deployDir}`);
1512
+ process.exit(1);
1513
+ }
1514
+ if (!fs.statSync(deployDir).isDirectory()) {
1515
+ error(`Not a directory: ${deployDir}`);
1516
+ process.exit(1);
1517
+ }
1518
+ const indexPath = path.join(deployDir, "index.html");
1519
+ if (!fs.existsSync(indexPath)) {
1520
+ error(`No index.html found in ${deployDir}`);
1521
+ log("");
1522
+ log("Your game needs an index.html file to deploy.");
1523
+ process.exit(1);
1524
+ }
1525
+ log(`Deploying ${colors.bright}${config.name}${colors.reset} from ${colors.dim}${deployDir}${colors.reset}`);
1526
+ try {
1527
+ const archiver = (await import("archiver")).default;
1528
+ const zipBuffer = await new Promise((resolve2, reject) => {
1529
+ const chunks = [];
1530
+ const archive = archiver("zip", { zlib: { level: 9 } });
1531
+ archive.on("data", (chunk) => chunks.push(chunk));
1532
+ archive.on("end", () => resolve2(Buffer.concat(chunks)));
1533
+ archive.on("error", reject);
1534
+ archive.glob("**/*", {
1535
+ cwd: deployDir,
1536
+ ignore: ["node_modules/**", ".starrc", ".git/**", ".DS_Store"],
1537
+ dot: false
1538
+ });
1539
+ archive.finalize();
1540
+ });
1541
+ log(` ${colors.dim}Uploading ${(zipBuffer.length / 1024).toFixed(1)} KB...${colors.reset}`);
1542
+ const response = await fetch(`${API_BASE}/api/sdk/games/${config.gameId}/deploy`, {
1543
+ method: "POST",
1544
+ headers: {
1545
+ "Content-Type": "application/zip",
1546
+ "x-deploy-key": config.deployKey
1547
+ },
1548
+ body: zipBuffer
1549
+ });
1550
+ if (!response.ok) {
1551
+ const data2 = await response.json().catch(() => ({}));
1552
+ if (response.status === 403) {
1553
+ error(data2.error || 'Invalid deploy key. Re-run "npx star-sdk init" to get a new one.');
1554
+ } else if (response.status === 404) {
1555
+ error("Game not found. It may have been deleted.");
1556
+ } else if (response.status === 413) {
1557
+ error(data2.error || "Upload too large (50MB limit).");
1558
+ } else if (response.status === 429) {
1559
+ error(data2.error || "Rate limit exceeded. Try again later.");
1560
+ } else {
1561
+ error(data2.error || `Deploy failed (HTTP ${response.status})`);
1562
+ }
1563
+ process.exit(1);
1564
+ }
1565
+ const data = await response.json();
1566
+ config.deployUrl = data.url;
1567
+ saveConfig(config);
1568
+ log("");
1569
+ success("Deployed successfully!");
1570
+ log("");
1571
+ log(` ${colors.dim}URL:${colors.reset} ${colors.cyan}${data.url}${colors.reset}`);
1572
+ log(` ${colors.dim}Files:${colors.reset} ${data.files} files, ${data.assets} assets`);
1573
+ log("");
1574
+ } catch (err) {
1575
+ if (err.code === "ECONNREFUSED" || err.code === "ENOTFOUND") {
1576
+ error("Could not connect to Star API. Check your internet connection.");
1577
+ } else {
1578
+ error(err.message || "Deploy failed");
1579
+ }
1580
+ process.exit(1);
1581
+ }
1582
+ }
1440
1583
  function transformForStdout(content) {
1441
1584
  return content.replace(/\[(\w+)\.md\]\(\.\/\1\.md\)/g, "$1 (`npx star-sdk docs $1`)").replace(/see (\w+)\.md/g, "run `npx star-sdk docs $1`").replace(
1442
1585
  /For detailed API documentation, see the linked files above\./,
@@ -1647,6 +1790,9 @@ async function main() {
1647
1790
  case "init":
1648
1791
  await initCommand(positional[0], email);
1649
1792
  break;
1793
+ case "deploy":
1794
+ await deployCommand(positional[0]);
1795
+ break;
1650
1796
  case "install": {
1651
1797
  const agent = positional[0] || "claude";
1652
1798
  const scope = flags.project ? "project" : "global";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "star-sdk-cli",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "CLI tool for Star SDK - register games and manage leaderboards",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,16 @@
18
18
  "build": "tsup",
19
19
  "dev": "tsx src/cli.ts"
20
20
  },
21
+ "license": "MIT",
22
+ "homepage": "https://buildwithstar.com/docs/sdk",
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/buildwithstar/star-sdk",
26
+ "directory": "packages/star-sdk-cli"
27
+ },
28
+ "bugs": {
29
+ "url": "https://github.com/buildwithstar/star-sdk/issues"
30
+ },
21
31
  "keywords": [
22
32
  "star",
23
33
  "sdk",
@@ -25,7 +35,11 @@
25
35
  "games",
26
36
  "leaderboard"
27
37
  ],
38
+ "dependencies": {
39
+ "archiver": "^7.0.0"
40
+ },
28
41
  "devDependencies": {
42
+ "@types/archiver": "^6.0.0",
29
43
  "tsup": "^8.0.0",
30
44
  "tsx": "^4.7.0",
31
45
  "typescript": "^5.4.5"