@couch-kit/cli 0.3.9 → 0.3.11
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 +3 -3
- package/dist/index.js +57 -67
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -47,11 +47,11 @@ android/app/src/main/assets/www/
|
|
|
47
47
|
Spawns headless WebSocket bots to simulate players (useful for load testing and quick iteration).
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
# Default: 4 bots, ws://localhost:8082
|
|
50
|
+
# Default: 4 bots, ws://localhost:8082/ws
|
|
51
51
|
bunx couch-kit simulate
|
|
52
52
|
|
|
53
|
-
# Custom host + count
|
|
54
|
-
bunx couch-kit simulate --url ws://192.168.1.99:8082 --count 8
|
|
53
|
+
# Custom host + count (the host serves WebSockets on the /ws path)
|
|
54
|
+
bunx couch-kit simulate --url ws://192.168.1.99:8082/ws --count 8
|
|
55
55
|
|
|
56
56
|
# Action interval (ms)
|
|
57
57
|
bunx couch-kit simulate --interval 250
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
2
|
+
var __esm = (fn, res, err) => () => {
|
|
3
|
+
if (fn)
|
|
4
|
+
try {
|
|
5
|
+
res = fn(fn = 0);
|
|
6
|
+
} catch (e) {
|
|
7
|
+
err = [e];
|
|
8
|
+
}
|
|
9
|
+
if (err)
|
|
10
|
+
throw err[0];
|
|
11
|
+
return res;
|
|
15
12
|
};
|
|
16
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
13
|
|
|
18
14
|
// ../../node_modules/.bun/commander@15.0.0/node_modules/commander/lib/error.js
|
|
19
15
|
var CommanderError, InvalidArgumentError;
|
|
@@ -129,7 +125,7 @@ class Help {
|
|
|
129
125
|
this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
|
|
130
126
|
}
|
|
131
127
|
visibleCommands(cmd) {
|
|
132
|
-
const visibleCommands = cmd.commands.filter((
|
|
128
|
+
const visibleCommands = cmd.commands.filter((cmd) => !cmd._hidden);
|
|
133
129
|
const helpCommand = cmd._getHelpCommand();
|
|
134
130
|
if (helpCommand && !helpCommand._hidden) {
|
|
135
131
|
visibleCommands.push(helpCommand);
|
|
@@ -602,8 +598,8 @@ class DualOptions {
|
|
|
602
598
|
}
|
|
603
599
|
}
|
|
604
600
|
function camelcase(str) {
|
|
605
|
-
return str.split("-").reduce((
|
|
606
|
-
return
|
|
601
|
+
return str.split("-").reduce((str, word) => {
|
|
602
|
+
return str + word[0].toUpperCase() + word.slice(1);
|
|
607
603
|
});
|
|
608
604
|
}
|
|
609
605
|
function splitOptionFlags(flags) {
|
|
@@ -1767,18 +1763,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1767
1763
|
this.error(message, { code: "commander.missingMandatoryOptionValue" });
|
|
1768
1764
|
}
|
|
1769
1765
|
_conflictingOption(option, conflictingOption) {
|
|
1770
|
-
const findBestOptionFromValue = (
|
|
1771
|
-
const optionKey =
|
|
1766
|
+
const findBestOptionFromValue = (option) => {
|
|
1767
|
+
const optionKey = option.attributeName();
|
|
1772
1768
|
const optionValue = this.getOptionValue(optionKey);
|
|
1773
1769
|
const negativeOption = this.options.find((target) => target.negate && optionKey === target.attributeName());
|
|
1774
1770
|
const positiveOption = this.options.find((target) => !target.negate && optionKey === target.attributeName());
|
|
1775
1771
|
if (negativeOption && (negativeOption.presetArg === undefined && optionValue === false || negativeOption.presetArg !== undefined && optionValue === negativeOption.presetArg)) {
|
|
1776
1772
|
return negativeOption;
|
|
1777
1773
|
}
|
|
1778
|
-
return positiveOption ||
|
|
1774
|
+
return positiveOption || option;
|
|
1779
1775
|
};
|
|
1780
|
-
const getErrorMessage = (
|
|
1781
|
-
const bestOption = findBestOptionFromValue(
|
|
1776
|
+
const getErrorMessage = (option) => {
|
|
1777
|
+
const bestOption = findBestOptionFromValue(option);
|
|
1782
1778
|
const optionKey = bestOption.attributeName();
|
|
1783
1779
|
const source = this.getOptionValueSource(optionKey);
|
|
1784
1780
|
if (source === "env") {
|
|
@@ -2116,7 +2112,7 @@ function generateId() {
|
|
|
2116
2112
|
if (typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function") {
|
|
2117
2113
|
const bytes = new Uint8Array(16);
|
|
2118
2114
|
globalThis.crypto.getRandomValues(bytes);
|
|
2119
|
-
return Array.from(bytes).map((
|
|
2115
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2120
2116
|
}
|
|
2121
2117
|
const a = Math.random().toString(36).substring(2, 15);
|
|
2122
2118
|
const b = Math.random().toString(36).substring(2, 10);
|
|
@@ -2165,11 +2161,14 @@ var init_src = __esm(() => {
|
|
|
2165
2161
|
init_middleware();
|
|
2166
2162
|
});
|
|
2167
2163
|
|
|
2168
|
-
// src/
|
|
2169
|
-
var
|
|
2170
|
-
|
|
2171
|
-
|
|
2164
|
+
// src/defaults.ts
|
|
2165
|
+
var DEFAULT_SIMULATE_URL;
|
|
2166
|
+
var init_defaults = __esm(() => {
|
|
2167
|
+
init_src();
|
|
2168
|
+
DEFAULT_SIMULATE_URL = `ws://localhost:${DEFAULT_HTTP_PORT + DEFAULT_WS_PORT_OFFSET}${DEFAULT_WS_PATH}`;
|
|
2172
2169
|
});
|
|
2170
|
+
|
|
2171
|
+
// src/commands/bundle.ts
|
|
2173
2172
|
import fs2 from "node:fs";
|
|
2174
2173
|
import path2 from "node:path";
|
|
2175
2174
|
import { execSync } from "node:child_process";
|
|
@@ -2250,15 +2249,12 @@ var init_bundle = __esm(() => {
|
|
|
2250
2249
|
});
|
|
2251
2250
|
|
|
2252
2251
|
// src/commands/simulate.ts
|
|
2253
|
-
var exports_simulate = {};
|
|
2254
|
-
__export(exports_simulate, {
|
|
2255
|
-
simulateCommand: () => simulateCommand
|
|
2256
|
-
});
|
|
2257
2252
|
var simulateCommand;
|
|
2258
2253
|
var init_simulate = __esm(() => {
|
|
2259
2254
|
init_commander();
|
|
2260
2255
|
init_src();
|
|
2261
|
-
|
|
2256
|
+
init_defaults();
|
|
2257
|
+
simulateCommand = new Command("simulate").description("Spawns headless bots to simulate players").option("-n, --count <number>", "Number of bots", "4").option("-u, --url <url>", "WebSocket URL of host", DEFAULT_SIMULATE_URL).option("-i, --interval <ms>", "Action interval in ms", "1000").action(async (options) => {
|
|
2262
2258
|
const count = parseInt(options.count);
|
|
2263
2259
|
const url = options.url;
|
|
2264
2260
|
const interval = parseInt(options.interval);
|
|
@@ -2315,10 +2311,6 @@ Stopping bots...`);
|
|
|
2315
2311
|
});
|
|
2316
2312
|
|
|
2317
2313
|
// src/commands/init.ts
|
|
2318
|
-
var exports_init = {};
|
|
2319
|
-
__export(exports_init, {
|
|
2320
|
-
initCommand: () => initCommand
|
|
2321
|
-
});
|
|
2322
2314
|
import fs3 from "node:fs";
|
|
2323
2315
|
import path3 from "node:path";
|
|
2324
2316
|
function detectPackageManager2() {
|
|
@@ -2358,14 +2350,15 @@ var init_init = __esm(() => {
|
|
|
2358
2350
|
dependencies: {
|
|
2359
2351
|
react: "^19.0.0",
|
|
2360
2352
|
"react-dom": "^19.0.0",
|
|
2361
|
-
"@couch-kit/client": "
|
|
2353
|
+
"@couch-kit/client": "latest",
|
|
2354
|
+
"@couch-kit/core": "latest"
|
|
2362
2355
|
},
|
|
2363
2356
|
devDependencies: {
|
|
2364
2357
|
"@types/react": "^19.0.0",
|
|
2365
2358
|
"@types/react-dom": "^19.0.0",
|
|
2366
2359
|
"@vitejs/plugin-react": "^6.0.0",
|
|
2367
2360
|
typescript: "^5.7.0",
|
|
2368
|
-
vite: "^
|
|
2361
|
+
vite: "^8.0.0"
|
|
2369
2362
|
}
|
|
2370
2363
|
};
|
|
2371
2364
|
fs3.writeFileSync(path3.join(targetDir, "package.json"), JSON.stringify(packageJson, null, 2) + `
|
|
@@ -2526,11 +2519,10 @@ Next steps:
|
|
|
2526
2519
|
});
|
|
2527
2520
|
|
|
2528
2521
|
// src/commands/replay.ts
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
replay: () => replay2
|
|
2532
|
-
});
|
|
2522
|
+
import { existsSync } from "node:fs";
|
|
2523
|
+
import { readFile } from "node:fs/promises";
|
|
2533
2524
|
import { resolve } from "node:path";
|
|
2525
|
+
import { pathToFileURL } from "node:url";
|
|
2534
2526
|
var replay2;
|
|
2535
2527
|
var init_replay = __esm(() => {
|
|
2536
2528
|
init_commander();
|
|
@@ -2538,25 +2530,23 @@ var init_replay = __esm(() => {
|
|
|
2538
2530
|
replay2 = new Command("replay").description("Replay a recorded game session against a reducer").argument("<recording>", "Path to recording JSON file").argument("<reducer>", "Path to reducer module (must export default or named 'reducer')").option("--snapshots", "Output intermediate state snapshots", false).option("--json", "Output as formatted JSON", false).action(async (recordingPath, reducerPath, options) => {
|
|
2539
2531
|
try {
|
|
2540
2532
|
const resolvedRecordingPath = resolve(recordingPath);
|
|
2541
|
-
|
|
2542
|
-
const exists = await recordingFile.exists();
|
|
2543
|
-
if (!exists) {
|
|
2533
|
+
if (!existsSync(resolvedRecordingPath)) {
|
|
2544
2534
|
console.error(`Error: Recording file not found: ${recordingPath}`);
|
|
2545
2535
|
process.exit(1);
|
|
2546
2536
|
}
|
|
2547
|
-
const recording = await
|
|
2537
|
+
const recording = JSON.parse(await readFile(resolvedRecordingPath, "utf-8"));
|
|
2548
2538
|
if (!recording.initialState || !Array.isArray(recording.actions)) {
|
|
2549
2539
|
console.error("Error: Invalid recording format. Expected { initialState, actions[] }");
|
|
2550
2540
|
process.exit(1);
|
|
2551
2541
|
}
|
|
2552
2542
|
const resolvedReducerPath = resolve(reducerPath);
|
|
2553
|
-
const reducerModule = await import(resolvedReducerPath);
|
|
2554
|
-
const
|
|
2555
|
-
if (typeof
|
|
2543
|
+
const reducerModule = await import(pathToFileURL(resolvedReducerPath).href);
|
|
2544
|
+
const reducer = reducerModule.default ?? reducerModule.reducer;
|
|
2545
|
+
if (typeof reducer !== "function") {
|
|
2556
2546
|
console.error("Error: Reducer module must export a default function or named 'reducer' export");
|
|
2557
2547
|
process.exit(1);
|
|
2558
2548
|
}
|
|
2559
|
-
const result = replayActions(recording,
|
|
2549
|
+
const result = replayActions(recording, reducer);
|
|
2560
2550
|
if (options.json) {
|
|
2561
2551
|
const output = options.snapshots ? result : {
|
|
2562
2552
|
finalState: result.finalState,
|
|
@@ -2587,10 +2577,6 @@ Snapshots (${result.snapshots.length}):`);
|
|
|
2587
2577
|
});
|
|
2588
2578
|
|
|
2589
2579
|
// src/commands/dev.ts
|
|
2590
|
-
var exports_dev = {};
|
|
2591
|
-
__export(exports_dev, {
|
|
2592
|
-
dev: () => dev
|
|
2593
|
-
});
|
|
2594
2580
|
import { resolve as resolve2 } from "node:path";
|
|
2595
2581
|
import { networkInterfaces } from "node:os";
|
|
2596
2582
|
function getLanIp() {
|
|
@@ -2654,36 +2640,37 @@ var init_dev = __esm(() => {
|
|
|
2654
2640
|
|
|
2655
2641
|
// src/index.ts
|
|
2656
2642
|
init_commander();
|
|
2643
|
+
init_defaults();
|
|
2657
2644
|
import { createRequire } from "node:module";
|
|
2658
2645
|
var require2 = createRequire(import.meta.url);
|
|
2659
2646
|
var { version } = require2("../package.json");
|
|
2660
2647
|
var program2 = new Command;
|
|
2661
2648
|
program2.name("couch-kit").description("CLI for Couch Kit").version(version);
|
|
2662
2649
|
program2.command("bundle").description("Bundles the web controller into the Android assets directory").option("-s, --source <path>", "Source directory of web controller", "./web-controller").option("-o, --output <path>", "Android assets directory", "./android/app/src/main/assets/www").option("--no-build", "Skip build step (just copy)").option("-m, --manifest <path>", "Also write manifest to this path for import in app source").action(async (_options, command) => {
|
|
2663
|
-
|
|
2664
|
-
await
|
|
2650
|
+
await Promise.resolve().then(() => init_bundle());
|
|
2651
|
+
await bundleCommand.parseAsync(reconstructArgs(command), {
|
|
2665
2652
|
from: "user"
|
|
2666
2653
|
});
|
|
2667
2654
|
});
|
|
2668
|
-
program2.command("simulate").description("Spawns headless bots to simulate players").option("-n, --count <number>", "Number of bots", "4").option("-u, --url <url>", "WebSocket URL of host",
|
|
2669
|
-
|
|
2670
|
-
await
|
|
2655
|
+
program2.command("simulate").description("Spawns headless bots to simulate players").option("-n, --count <number>", "Number of bots", "4").option("-u, --url <url>", "WebSocket URL of host", DEFAULT_SIMULATE_URL).option("-i, --interval <ms>", "Action interval in ms", "1000").action(async (_options, command) => {
|
|
2656
|
+
await Promise.resolve().then(() => init_simulate());
|
|
2657
|
+
await simulateCommand.parseAsync(reconstructArgs(command), {
|
|
2671
2658
|
from: "user"
|
|
2672
2659
|
});
|
|
2673
2660
|
});
|
|
2674
2661
|
program2.command("init").description("Scaffolds a new web controller project").argument("[name]", "Project name", "web-controller").action(async (name) => {
|
|
2675
|
-
|
|
2676
|
-
await
|
|
2662
|
+
await Promise.resolve().then(() => init_init());
|
|
2663
|
+
await initCommand.parseAsync([name], { from: "user" });
|
|
2677
2664
|
});
|
|
2678
|
-
program2.command("replay").description("Replay a recorded game session against a reducer").argument("<recording>", "Path to recording JSON file").argument("<reducer>", "Path to reducer module").option("--snapshots", "Output intermediate state snapshots").option("--json", "Output as formatted JSON").action(async (recording,
|
|
2679
|
-
|
|
2680
|
-
await
|
|
2665
|
+
program2.command("replay").description("Replay a recorded game session against a reducer").argument("<recording>", "Path to recording JSON file").argument("<reducer>", "Path to reducer module").option("--snapshots", "Output intermediate state snapshots").option("--json", "Output as formatted JSON").action(async (recording, reducer, _options, command) => {
|
|
2666
|
+
await Promise.resolve().then(() => init_replay());
|
|
2667
|
+
await replay2.parseAsync([recording, reducer, ...reconstructArgs(command)], {
|
|
2681
2668
|
from: "user"
|
|
2682
2669
|
});
|
|
2683
2670
|
});
|
|
2684
2671
|
program2.command("dev").description("Start development server with LAN access").option("-p, --port <port>", "Port number", "5173").option("--host", "Expose to LAN").option("--open", "Open browser automatically").action(async (_options, command) => {
|
|
2685
|
-
|
|
2686
|
-
await
|
|
2672
|
+
await Promise.resolve().then(() => init_dev());
|
|
2673
|
+
await dev.parseAsync(reconstructArgs(command), {
|
|
2687
2674
|
from: "user"
|
|
2688
2675
|
});
|
|
2689
2676
|
});
|
|
@@ -2691,9 +2678,12 @@ function reconstructArgs(command) {
|
|
|
2691
2678
|
const args = [];
|
|
2692
2679
|
const opts = command.opts();
|
|
2693
2680
|
for (const option of command.options) {
|
|
2694
|
-
const
|
|
2681
|
+
const name = option.attributeName();
|
|
2682
|
+
const value = opts[name];
|
|
2695
2683
|
if (value === undefined)
|
|
2696
2684
|
continue;
|
|
2685
|
+
if (command.getOptionValueSource(name) === "default")
|
|
2686
|
+
continue;
|
|
2697
2687
|
if (option.negate) {
|
|
2698
2688
|
if (value === false)
|
|
2699
2689
|
args.push(option.long ?? option.short ?? "");
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@couch-kit/cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"provenance": true
|
|
7
7
|
},
|
|
8
8
|
"description": "CLI tools for Couch Kit party games — bundle web controllers, simulate clients, and scaffold projects",
|
|
9
9
|
"license": "MIT",
|
|
10
|
+
"type": "module",
|
|
10
11
|
"engines": {
|
|
11
|
-
"node": ">=
|
|
12
|
+
"node": ">=22.0.0"
|
|
12
13
|
},
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -33,16 +34,15 @@
|
|
|
33
34
|
"scripts": {
|
|
34
35
|
"build": "bun build ./src/index.ts --outdir ./dist --target node",
|
|
35
36
|
"test": "bun test",
|
|
36
|
-
"lint": "eslint src/",
|
|
37
37
|
"typecheck": "tsc --noEmit",
|
|
38
38
|
"prepublishOnly": "bun run build",
|
|
39
39
|
"clean": "rm -rf dist"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@couch-kit/core": "0.
|
|
42
|
+
"@couch-kit/core": "0.10.0",
|
|
43
43
|
"commander": "^15.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"typescript": "^
|
|
46
|
+
"typescript": "^7.0.0"
|
|
47
47
|
}
|
|
48
48
|
}
|