star-sdk-cli 0.1.12 → 0.1.14
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 +52 -0
- package/dist/cli.mjs +96 -14
- package/package.json +5 -3
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# star-sdk-cli
|
|
2
|
+
|
|
3
|
+
CLI for [Star SDK](https://www.npmjs.com/package/star-sdk) — register games, deploy to hosting, and install AI coding docs.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
All commands run via `npx star-sdk` (no global install needed).
|
|
8
|
+
|
|
9
|
+
### Commands
|
|
10
|
+
|
|
11
|
+
| Command | Description |
|
|
12
|
+
|---------|-------------|
|
|
13
|
+
| `npx star-sdk init "Game Name"` | Register a game, get a gameId and deploy key |
|
|
14
|
+
| `npx star-sdk deploy [path]` | Deploy your game to Star hosting |
|
|
15
|
+
| `npx star-sdk install [agent]` | Install SDK docs for AI coding tools |
|
|
16
|
+
| `npx star-sdk docs [topic]` | Print API docs (audio, canvas, leaderboard) |
|
|
17
|
+
| `npx star-sdk whoami` | Show current game config |
|
|
18
|
+
|
|
19
|
+
### Workflow
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# 1. Register your game
|
|
23
|
+
npx star-sdk init "My Game"
|
|
24
|
+
# Creates .starrc with gameId and deploy key
|
|
25
|
+
|
|
26
|
+
# 2. Build your game (use the gameId from .starrc)
|
|
27
|
+
# ... write your game code ...
|
|
28
|
+
|
|
29
|
+
# 3. Deploy
|
|
30
|
+
npx star-sdk deploy
|
|
31
|
+
# => Live at https://buildwithstar.com/games/<id>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### AI Agent Support
|
|
35
|
+
|
|
36
|
+
Install SDK documentation directly into your AI coding tool:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx star-sdk install # Claude Code (default)
|
|
40
|
+
npx star-sdk install cursor # Cursor
|
|
41
|
+
npx star-sdk install codex # OpenAI Codex
|
|
42
|
+
npx star-sdk install windsurf # Windsurf
|
|
43
|
+
npx star-sdk install aider # Aider
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Documentation
|
|
47
|
+
|
|
48
|
+
Full docs at [buildwithstar.com/docs/sdk](https://buildwithstar.com/docs/sdk)
|
|
49
|
+
|
|
50
|
+
## License
|
|
51
|
+
|
|
52
|
+
MIT
|
package/dist/cli.mjs
CHANGED
|
@@ -231,6 +231,16 @@ Complete working games are in the \`examples/\` directory (also published with t
|
|
|
231
231
|
- **reaction-time** \u2014 Test your reflexes over 5 rounds, DOM-based (no canvas)
|
|
232
232
|
|
|
233
233
|
Each example is a single HTML file, no build step \u2014 imports from esm.sh.
|
|
234
|
+
|
|
235
|
+
## Deploy
|
|
236
|
+
|
|
237
|
+
When the game is ready, deploy it with one command:
|
|
238
|
+
|
|
239
|
+
\`\`\`bash
|
|
240
|
+
npx star-sdk deploy
|
|
241
|
+
\`\`\`
|
|
242
|
+
|
|
243
|
+
This uploads the game to Star hosting and returns a live URL. Requires \`npx star-sdk init\` to have been run first (which creates the deploy key in \`.starrc\`).
|
|
234
244
|
`;
|
|
235
245
|
var AUDIO_DOCS = `**Installation**
|
|
236
246
|
|
|
@@ -413,24 +423,22 @@ game(({ ctx, width, height, on, loop, ui, canvas }) => {
|
|
|
413
423
|
});
|
|
414
424
|
|
|
415
425
|
// 2. Render HTML to the safe UI overlay
|
|
416
|
-
// UI is interactive by default (scroll, buttons work)
|
|
417
|
-
// Adding canvas.addEventListener makes UI click-through automatically
|
|
418
426
|
ui.render(\`
|
|
419
427
|
<div class="absolute top-4 left-4 text-white">
|
|
420
|
-
<button id="start-btn" class="px-4 py-2 bg-blue-500 rounded
|
|
428
|
+
<button id="start-btn" class="px-4 py-2 bg-blue-500 rounded">
|
|
421
429
|
Click Me
|
|
422
430
|
</button>
|
|
423
431
|
</div>
|
|
424
432
|
\`);
|
|
425
433
|
|
|
426
|
-
// 3. Listen for button clicks
|
|
434
|
+
// 3. Listen for button clicks \u2014 on() auto-enables pointer-events for the target
|
|
427
435
|
on('click', '#start-btn', () => {
|
|
428
436
|
console.log('Button clicked!');
|
|
429
437
|
});
|
|
430
438
|
|
|
431
439
|
// 4. For canvas games: listen for taps on canvas
|
|
432
|
-
//
|
|
433
|
-
//
|
|
440
|
+
// Taps pass through the UI overlay to the canvas layer
|
|
441
|
+
// Elements targeted by on() are automatically interactive
|
|
434
442
|
canvas.addEventListener('pointerdown', (e) => {
|
|
435
443
|
console.log('Canvas/screen tapped!', e);
|
|
436
444
|
});
|
|
@@ -495,7 +503,7 @@ A safe manager for your HTML overlay, stacked on top of the canvas.
|
|
|
495
503
|
- \`ui.el(selector)\`: Scoped \`querySelector\` for the UI root.
|
|
496
504
|
- \`ui.all(selector)\`: Scoped \`querySelectorAll\` for the UI root.
|
|
497
505
|
|
|
498
|
-
**Auto-detection:** When you add \`canvas.addEventListener('pointerdown', ...)\`, the SDK automatically makes UI click-through so taps reach the canvas.
|
|
506
|
+
**Auto-detection:** When you add \`canvas.addEventListener('pointerdown', ...)\`, the SDK automatically makes UI click-through so taps reach the canvas. Elements targeted by \`on()\` are automatically interactive \u2014 no extra CSS classes needed. Native \`<button>\` and \`<a>\` elements are also always interactive.
|
|
499
507
|
|
|
500
508
|
### Cursor Management
|
|
501
509
|
|
|
@@ -723,13 +731,13 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
|
|
|
723
731
|
// 1. Listen for screen taps - this makes UI click-through automatically
|
|
724
732
|
canvas.addEventListener('pointerdown', handleTap);
|
|
725
733
|
|
|
726
|
-
// 2. Listen for button clicks
|
|
734
|
+
// 2. Listen for button clicks \u2014 on() auto-enables pointer-events for the selector
|
|
727
735
|
on('click', '#leaderboard-btn', (e) => {
|
|
728
736
|
e.stopPropagation();
|
|
729
737
|
leaderboard.show();
|
|
730
738
|
});
|
|
731
739
|
|
|
732
|
-
// 3. Render UI
|
|
740
|
+
// 3. Render UI \u2014 elements targeted by on() are automatically interactive
|
|
733
741
|
let lastState = null;
|
|
734
742
|
let lastScore = -1;
|
|
735
743
|
|
|
@@ -756,7 +764,7 @@ game(({ ctx, width, height, loop, ui, on, canvas, toStagePoint }) => {
|
|
|
756
764
|
<div class="h-full flex flex-col items-center justify-center text-white">
|
|
757
765
|
<div class="text-3xl mb-4">GAME OVER</div>
|
|
758
766
|
<div class="text-6xl mb-4">\\\${score}</div>
|
|
759
|
-
<button id="leaderboard-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20
|
|
767
|
+
<button id="leaderboard-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20">
|
|
760
768
|
VIEW LEADERBOARD
|
|
761
769
|
</button>
|
|
762
770
|
<div class="text-xl animate-pulse">TAP TO RESTART</div>
|
|
@@ -1098,7 +1106,7 @@ game(({ ctx, width, height, loop, ui, on, canvas }) => {
|
|
|
1098
1106
|
- Score submission (works for guests and logged-in users)
|
|
1099
1107
|
- Leaderboard UI (modal with rankings)
|
|
1100
1108
|
- Weekly/all-time timeframes
|
|
1101
|
-
-
|
|
1109
|
+
- Configurable sort order (\`'asc'\` for lower-is-better, \`'desc'\` for higher-is-better)
|
|
1102
1110
|
|
|
1103
1111
|
---
|
|
1104
1112
|
|
|
@@ -1170,7 +1178,7 @@ const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>' });
|
|
|
1170
1178
|
|
|
1171
1179
|
game(({ ui, on }) => {
|
|
1172
1180
|
ui.render(\`
|
|
1173
|
-
<button id="lb-btn" class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold text-white shadow-lg shadow-blue-500/20
|
|
1181
|
+
<button id="lb-btn" class="px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold text-white shadow-lg shadow-blue-500/20">
|
|
1174
1182
|
View Leaderboard
|
|
1175
1183
|
</button>
|
|
1176
1184
|
\`);
|
|
@@ -1237,7 +1245,7 @@ game(({ ctx, width, height, loop, ui, on, canvas }) => {
|
|
|
1237
1245
|
<div class="h-full flex flex-col items-center justify-center text-white">
|
|
1238
1246
|
<div class="text-3xl mb-4">GAME OVER</div>
|
|
1239
1247
|
<div class="text-6xl mb-4">\\\${score}</div>
|
|
1240
|
-
<button id="lb-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20
|
|
1248
|
+
<button id="lb-btn" class="px-6 py-3 mb-4 bg-gradient-to-r from-blue-600 to-purple-600 rounded-xl font-bold shadow-lg shadow-blue-500/20">
|
|
1241
1249
|
VIEW LEADERBOARD
|
|
1242
1250
|
</button>
|
|
1243
1251
|
<div class="text-xl animate-pulse">TAP TO RESTART</div>
|
|
@@ -1289,6 +1297,27 @@ const data = await leaderboard.getScores({
|
|
|
1289
1297
|
|
|
1290
1298
|
---
|
|
1291
1299
|
|
|
1300
|
+
**Sort Order:**
|
|
1301
|
+
|
|
1302
|
+
By default, leaderboards rank higher scores first (\`'desc'\`). For games where lower is better (reaction time, speedruns, golf), set \`sort: 'asc'\`:
|
|
1303
|
+
|
|
1304
|
+
\`\`\`javascript
|
|
1305
|
+
const leaderboard = createLeaderboard({ gameId: '<gameId from .starrc>', sort: 'asc' });
|
|
1306
|
+
\`\`\`
|
|
1307
|
+
|
|
1308
|
+
**IMPORTANT: Do NOT invert scores to fake ascending order.** Use \`sort: 'asc'\` instead.
|
|
1309
|
+
|
|
1310
|
+
\`\`\`javascript
|
|
1311
|
+
// BAD \u2014 do not do this
|
|
1312
|
+
leaderboard.submit(10000 - reactionTimeMs);
|
|
1313
|
+
|
|
1314
|
+
// GOOD \u2014 submit the real value with sort: 'asc'
|
|
1315
|
+
const leaderboard = createLeaderboard({ gameId, sort: 'asc' });
|
|
1316
|
+
leaderboard.submit(reactionTimeMs);
|
|
1317
|
+
\`\`\`
|
|
1318
|
+
|
|
1319
|
+
---
|
|
1320
|
+
|
|
1292
1321
|
**Tips:**
|
|
1293
1322
|
|
|
1294
1323
|
1. **Call \`submit()\` before \`show()\`** - Ensures your score appears immediately in the leaderboard.
|
|
@@ -1327,6 +1356,52 @@ function error(message) {
|
|
|
1327
1356
|
function info(message) {
|
|
1328
1357
|
console.log(`${colors.blue}\u2139${colors.reset} ${message}`);
|
|
1329
1358
|
}
|
|
1359
|
+
var STAR_PACKAGES = ["star-sdk", "star-canvas", "star-audio", "star-leaderboard", "star-multiplayer"];
|
|
1360
|
+
function resolveStarSdkVersion(deployDir) {
|
|
1361
|
+
const candidates = [
|
|
1362
|
+
path.join(deployDir, "node_modules", "star-sdk", "package.json"),
|
|
1363
|
+
path.join(process.cwd(), "node_modules", "star-sdk", "package.json")
|
|
1364
|
+
];
|
|
1365
|
+
for (const p of candidates) {
|
|
1366
|
+
try {
|
|
1367
|
+
if (fs.existsSync(p)) {
|
|
1368
|
+
const pkg = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
1369
|
+
if (pkg.version) return pkg.version;
|
|
1370
|
+
}
|
|
1371
|
+
} catch {
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return null;
|
|
1375
|
+
}
|
|
1376
|
+
function injectImportMapIfNeeded(html, deployDir) {
|
|
1377
|
+
if (/<script\s[^>]*type\s*=\s*["']importmap["'][^>]*>/i.test(html)) {
|
|
1378
|
+
return html;
|
|
1379
|
+
}
|
|
1380
|
+
const bareImportPattern = /\bfrom\s+['"](?:star-sdk|star-canvas|star-audio|star-leaderboard|star-multiplayer)['"]/;
|
|
1381
|
+
if (!bareImportPattern.test(html)) {
|
|
1382
|
+
return html;
|
|
1383
|
+
}
|
|
1384
|
+
const version = resolveStarSdkVersion(deployDir);
|
|
1385
|
+
const suffix = version ? `@${version}` : "";
|
|
1386
|
+
const imports = {};
|
|
1387
|
+
for (const pkg of STAR_PACKAGES) {
|
|
1388
|
+
imports[pkg] = `https://esm.sh/${pkg}${suffix}`;
|
|
1389
|
+
}
|
|
1390
|
+
const importmapTag = `<script type="importmap">
|
|
1391
|
+
${JSON.stringify({ imports }, null, 2)}
|
|
1392
|
+
</script>
|
|
1393
|
+
`;
|
|
1394
|
+
const moduleScriptMatch = html.match(/<script\s[^>]*type\s*=\s*["']module["'][^>]*>/i);
|
|
1395
|
+
if (moduleScriptMatch && moduleScriptMatch.index !== void 0) {
|
|
1396
|
+
return html.slice(0, moduleScriptMatch.index) + importmapTag + html.slice(moduleScriptMatch.index);
|
|
1397
|
+
}
|
|
1398
|
+
const headMatch = html.match(/<head[^>]*>/i);
|
|
1399
|
+
if (headMatch && headMatch.index !== void 0) {
|
|
1400
|
+
const insertPos = headMatch.index + headMatch[0].length;
|
|
1401
|
+
return html.slice(0, insertPos) + "\n" + importmapTag + html.slice(insertPos);
|
|
1402
|
+
}
|
|
1403
|
+
return html;
|
|
1404
|
+
}
|
|
1330
1405
|
function showHelp() {
|
|
1331
1406
|
log(`
|
|
1332
1407
|
${colors.bright}Star SDK CLI${colors.reset} v${VERSION}
|
|
@@ -1524,6 +1599,12 @@ async function deployCommand(dirPath) {
|
|
|
1524
1599
|
}
|
|
1525
1600
|
log(`Deploying ${colors.bright}${config.name}${colors.reset} from ${colors.dim}${deployDir}${colors.reset}`);
|
|
1526
1601
|
try {
|
|
1602
|
+
const originalHtml = fs.readFileSync(indexPath, "utf-8");
|
|
1603
|
+
const processedHtml = injectImportMapIfNeeded(originalHtml, deployDir);
|
|
1604
|
+
const htmlModified = processedHtml !== originalHtml;
|
|
1605
|
+
if (htmlModified) {
|
|
1606
|
+
info("Injected importmap for bare imports (star-sdk \u2192 esm.sh)");
|
|
1607
|
+
}
|
|
1527
1608
|
const archiver = (await import("archiver")).default;
|
|
1528
1609
|
const zipBuffer = await new Promise((resolve2, reject) => {
|
|
1529
1610
|
const chunks = [];
|
|
@@ -1533,9 +1614,10 @@ async function deployCommand(dirPath) {
|
|
|
1533
1614
|
archive.on("error", reject);
|
|
1534
1615
|
archive.glob("**/*", {
|
|
1535
1616
|
cwd: deployDir,
|
|
1536
|
-
ignore: ["node_modules/**", ".starrc", ".git/**", ".DS_Store"],
|
|
1617
|
+
ignore: ["node_modules/**", ".starrc", ".git/**", ".DS_Store", "index.html"],
|
|
1537
1618
|
dot: false
|
|
1538
1619
|
});
|
|
1620
|
+
archive.append(processedHtml, { name: "index.html" });
|
|
1539
1621
|
archive.finalize();
|
|
1540
1622
|
});
|
|
1541
1623
|
log(` ${colors.dim}Uploading ${(zipBuffer.length / 1024).toFixed(1)} KB...${colors.reset}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "star-sdk-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "CLI
|
|
3
|
+
"version": "0.1.14",
|
|
4
|
+
"description": "CLI for Star SDK — register games, install AI docs, and deploy to Star hosting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"star-sdk": "./dist/cli.mjs"
|
|
@@ -33,7 +33,9 @@
|
|
|
33
33
|
"sdk",
|
|
34
34
|
"cli",
|
|
35
35
|
"games",
|
|
36
|
-
"leaderboard"
|
|
36
|
+
"leaderboard",
|
|
37
|
+
"deploy",
|
|
38
|
+
"hosting"
|
|
37
39
|
],
|
|
38
40
|
"dependencies": {
|
|
39
41
|
"archiver": "^7.0.0"
|