@coderook/cli 0.34.11 → 0.36.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.
@@ -2,7 +2,7 @@
2
2
  "name": "coderook",
3
3
  "displayName": "CodeRook",
4
4
  "description": "Save, browse and restore whole-snapshot versions of a project on CodeRook, with cbx or plain git, from Claude Code.",
5
- "version": "0.34.11",
5
+ "version": "0.36.0",
6
6
  "author": {
7
7
  "name": "ACCA Gaming Productions",
8
8
  "url": "https://coderook.com"
package/README.md CHANGED
@@ -194,6 +194,33 @@ Tests whenever a line is proposed or saved onto, and its result is a required
194
194
  check. `--on-save` runs an action on every save, and `--by-hand` only when
195
195
  somebody starts it.
196
196
 
197
+ Every proposal also has a `coderook/malware` check: what the line adds or
198
+ changes is scanned before it can land, and a finding names the file and stops
199
+ the proposal being applied.
200
+
201
+ ### Reviewing proposals on GitHub
202
+
203
+ CodeRabbit, Blacksmith and most CI only connect to GitHub, so `cbx github`
204
+ keeps a GitHub repository as a mirror: each open proposal becomes a pull
205
+ request there, whatever is installed on it reviews and builds it, and the
206
+ results come back to the proposal as checks (`github/<check>`,
207
+ `review/<reviewer>`).
208
+
209
+ ```
210
+ cbx github setup my-name/my-game-mirror token, secret, workflow
211
+ cbx submit save the workflow into the project
212
+ cbx github sync --repo my-name/my-game-mirror mirror once from here
213
+ ```
214
+
215
+ `setup` makes a token that reaches this project only and can only read it and
216
+ report checks, stores it as the repository's `CODEROOK_TOKEN` secret (with the
217
+ GitHub CLI; otherwise it prints it once), and writes
218
+ `.github/workflows/coderook-bridge.yml`, which then runs on GitHub every ten
219
+ minutes. Add `CODEROOK_BRIDGE_PUSH`, a fine-grained GitHub token with Contents
220
+ and Pull requests read and write: GitHub's own workflow token cannot start
221
+ other workflows, so CI would never run. Results are advice unless named in
222
+ `CODEROOK_REQUIRED` (or `--required`). Use a repository nothing else pushes to.
223
+
197
224
  A **protected** line refuses saves made straight onto it — from `cbx submit`,
198
225
  the desktop application and `git push` alike — so the way in is a line of your
199
226
  own and a proposal. It also cannot be put back on an earlier save, and a merge
@@ -452,6 +479,10 @@ no need for a second project that only holds releases. `--downloads private`
452
479
  takes them back. Without a release name it changes an existing release and
453
480
  leaves its notes alone.
454
481
 
482
+ Every attached file is scanned for malware before anybody outside the project
483
+ can download it, usually within a minute; one that fails is never offered, and
484
+ whoever attached it and the owner are told what was found.
485
+
455
486
  `cbx releases` marks each one `public`, `downloads public` or `private`, and
456
487
  for a public project prints the addresses an app or a script reads, which
457
488
  need no account:
@@ -462,6 +493,46 @@ https://api.coderook.com/v1/public/projects/OWNER/SLUG/releases
462
493
  https://api.coderook.com/v1/public/projects/OWNER/SLUG/updates electron-updater's generic provider
463
494
  ```
464
495
 
496
+ ## Mod repositories for games
497
+
498
+ A mod repository is where a game's players get mods from. The game's maker
499
+ sets one up and its rules; modders publish with their own accounts; nothing is
500
+ listed until it passes those rules and a malware scan.
501
+
502
+ ```
503
+ cbx mods create my-game --name "My Game" --allow .png,.ogg,.json --nested .pak
504
+ cbx mods builds my-game --new a build secret for the game, shown once
505
+ cbx mods builds my-game --register secret.bin or your own, never printed
506
+ cbx mods info my-game the address and public key to pin
507
+ cbx mods settings my-game --uploads owner change the rules
508
+ ```
509
+
510
+ Rules: `--allow` and `--block` for file types (programs and scripts such as
511
+ `.exe`, `.dll`, `.ps1` and `.js` are refused in every repository, and so is a
512
+ file that is really a program whatever it is called), `--nested` for archive
513
+ types that are opened and checked too, `--manifest` and `--fields` for the
514
+ manifest each part carries (`mod.json` by default) and which of its fields must
515
+ match, `--max-part`, `--max-files`, `--max-parts` and `--max-ratio` for size,
516
+ and `--uploads anyone|owner`.
517
+
518
+ ```
519
+ cbx mods publish ./BetterTrees --repo my-game zip, upload, wait for the checks
520
+ cbx mods publish ./HD-Art --repo my-game --part animals one part of a bigger mod
521
+ cbx mods list my-game your mods and how their checks went
522
+ cbx mods remove better-trees --repo my-game
523
+ ```
524
+
525
+ `publish` reads the folder's manifest for the mod's id, name, folder and
526
+ version (`--id`, `--name`, `--folder` and `--version` override it), writes the
527
+ manifest the repository expects into the zip, uploads in pieces that are
528
+ retried, and waits up to 30 minutes for the result; `--no-wait` returns
529
+ straight away. A rejected part says what was wrong, file by file.
530
+
531
+ The owner also has `cbx mods reports`, `cbx mods block <username> --take-down`,
532
+ `cbx mods list --all`, `cbx mods remove --as-owner` and `cbx mods delete`.
533
+ How the game signs its requests and checks the catalogue is at
534
+ <https://coderook.com/docs#mod-repositories>.
535
+
465
536
  ## Building for every platform
466
537
 
467
538
  ```
@@ -1614,10 +1614,29 @@ const SECRET_SUFFIXES = [".pem", ".key", ".p12", ".pfx", ".keystore", ".jks"];
1614
1614
  the answer be a rule — one line in .gitignore covers the lot, where a list
1615
1615
  of the files inside it would go stale the moment the program ran again.
1616
1616
  */
1617
+ /** Chromium's disk cache: an index beside data_N blocks, Cache_Data, or the js/wasm code cache. */
1618
+ async function looksLikeBrowserCache(directory) {
1619
+ const names = new Set((await (0, promises_1.readdir)(directory).catch(() => [])).map((name) => name.toLowerCase()));
1620
+ if (names.has("cache_data") || names.has("index-dir"))
1621
+ return true;
1622
+ if (names.has("index") && names.has("data_0"))
1623
+ return true;
1624
+ return names.has("js") && names.has("wasm");
1625
+ }
1617
1626
  const PRIVATE_DIRECTORIES = [
1618
1627
  {
1619
- match: /^(Local Storage|Session Storage|IndexedDB|Service Worker|Cache|Code Cache|GPUCache)$/i,
1628
+ match: /^(Local Storage|Session Storage|IndexedDB|Service Worker)$/i,
1629
+ because: "a browser profile, which holds the sites you are signed in to",
1630
+ },
1631
+ /*
1632
+ "Cache" is also an ordinary name for source: FreeType keeps ftcache.c in
1633
+ `src/cache`, and a Godot checkout was refused as "a browser profile" for
1634
+ it. So these three only count when what is inside is a browser's cache.
1635
+ */
1636
+ {
1637
+ match: /^(Cache|Code Cache|GPUCache)$/i,
1620
1638
  because: "a browser profile, which holds the sites you are signed in to",
1639
+ inside: looksLikeBrowserCache,
1621
1640
  },
1622
1641
  { match: /^\.wrangler$/i, because: "local service state, including emulated databases" },
1623
1642
  { match: /^\.aws$/i, because: "cloud credentials" },
@@ -1667,7 +1686,15 @@ async function detectPrivateDirectories(root) {
1667
1686
  continue;
1668
1687
  const full = node_path_1.default.join(directory, entry.name);
1669
1688
  const relative = node_path_1.default.relative(root, full).split(node_path_1.default.sep).join("/");
1670
- const rule = PRIVATE_DIRECTORIES.find((candidate) => candidate.match.test(entry.name));
1689
+ let rule;
1690
+ for (const candidate of PRIVATE_DIRECTORIES) {
1691
+ if (!candidate.match.test(entry.name))
1692
+ continue;
1693
+ if (candidate.inside && !(await candidate.inside(full)))
1694
+ continue;
1695
+ rule = candidate;
1696
+ break;
1697
+ }
1671
1698
  if (rule) {
1672
1699
  found.push({
1673
1700
  path: relative,
@@ -45,6 +45,8 @@ exports.changeWorkflow = changeWorkflow;
45
45
  exports.runs = runs;
46
46
  exports.runLogs = runLogs;
47
47
  exports.tokens = tokens;
48
+ exports.createToken = createToken;
49
+ exports.reportMergeCheck = reportMergeCheck;
48
50
  exports.revokeToken = revokeToken;
49
51
  exports.deleteProject = deleteProject;
50
52
  exports.projectLabels = projectLabels;
@@ -806,6 +808,20 @@ async function tokens() {
806
808
  createdAt: String(row.createdAt ?? ""),
807
809
  }));
808
810
  }
811
+ /** Make a token. Limited to one project when `repositoryId` is given; shown once. */
812
+ async function createToken(input) {
813
+ return call("/v1/account/tokens", {
814
+ method: "POST",
815
+ body: input,
816
+ });
817
+ }
818
+ /** Say how a check on a proposal went; the same name again replaces it. */
819
+ async function reportMergeCheck(mergeTrackId, name, input) {
820
+ await call(`/v1/merge-tracks/${mergeTrackId}/checks/${encodeURIComponent(name)}`, {
821
+ method: "PUT",
822
+ body: input,
823
+ });
824
+ }
809
825
  async function revokeToken(tokenId) {
810
826
  await call(`/v1/account/tokens/${encodeURIComponent(tokenId)}`, {
811
827
  method: "DELETE",
@@ -31,6 +31,8 @@ const attach_command_js_1 = require("./attach_command.js");
31
31
  const build_command_js_1 = require("./build_command.js");
32
32
  const node_check_js_1 = require("./node_check.js");
33
33
  const pages_command_js_1 = require("./pages_command.js");
34
+ const mods_command_js_1 = require("./mods_command.js");
35
+ const github_bridge_js_1 = require("./github_bridge.js");
34
36
  const project_commands_js_1 = require("./project_commands.js");
35
37
  const track_commands_js_1 = require("./track_commands.js");
36
38
  const tracks_js_1 = require("../../cbx/src/core/tracks.js");
@@ -2792,6 +2794,101 @@ const SPECS = [
2792
2794
  ],
2793
2795
  run: pages_command_js_1.commandPages,
2794
2796
  },
2797
+ {
2798
+ name: "github",
2799
+ group: "Your projects",
2800
+ summary: "review and test proposals with GitHub tools (CodeRabbit, Blacksmith, Actions)",
2801
+ usage: "github <setup|sync> …",
2802
+ detail: "Mirrors a project's open proposals to pull requests in a GitHub repository,\n" +
2803
+ "so tools that only connect to GitHub review and build them, and reports\n" +
2804
+ "what they conclude back to each proposal as checks. A required check that\n" +
2805
+ "fails stops the proposal being applied, like any other check.\n\n" +
2806
+ "It runs on GitHub, in a workflow setup writes into the project. CodeRook\n" +
2807
+ "never holds a GitHub credential; GitHub holds a CodeRook token that reaches\n" +
2808
+ "this project only and can read it and report checks, nothing else.\n\n" +
2809
+ " setup <owner>/<repo> [folder] make the token, write the workflow\n" +
2810
+ " sync [--repo <owner>/<repo>] mirror and report once (what the workflow runs)\n\n" +
2811
+ "Use a GitHub repository nothing else pushes to: the mirror only moves\n" +
2812
+ "forward, and a branch changed on GitHub is left alone and reported.",
2813
+ options: [
2814
+ { flags: "--repo <owner/repo>", description: "sync: the GitHub repository (default: the one the workflow runs in)" },
2815
+ { flags: "--project <slug>", description: "sync: the CodeRook project (default: this folder's)" },
2816
+ { flags: "--required <names>", description: "checks that must pass, e.g. \"github/build,review/coderabbitai[bot]\"" },
2817
+ { flags: "--force", description: "setup: write the workflow again" },
2818
+ ],
2819
+ examples: [
2820
+ "cbx github setup my-name/my-game-mirror",
2821
+ "cbx submit",
2822
+ "cbx github sync --repo my-name/my-game-mirror",
2823
+ ],
2824
+ run: github_bridge_js_1.commandGithub,
2825
+ },
2826
+ {
2827
+ name: "mods",
2828
+ group: "Your projects",
2829
+ summary: "mod repositories for games: set one up, publish to one",
2830
+ usage: "mods <create|repos|info|settings|builds|list|publish|remove|block|reports|delete> …",
2831
+ detail: "A mod repository is where a game's players get mods from. Its owner\n" +
2832
+ "sets the rules — which file types a mod may hold, archives opened and\n" +
2833
+ "checked too, the manifest each upload carries, size limits, and whether\n" +
2834
+ "anybody or only the owner may upload. Modders publish with their own\n" +
2835
+ "CodeRook accounts; nothing is listed until it passes the rules and a\n" +
2836
+ "malware scan, and anything that runs (.exe, .dll, scripts) is refused\n" +
2837
+ "in every repository. The game's client reads a catalogue signed with\n" +
2838
+ "the repository's own key: see coderook.com/docs#mod-repositories.\n\n" +
2839
+ " create <repo> --name \"Game\" set one up; prints its address and public key\n" +
2840
+ " repos the repositories you run\n" +
2841
+ " info <repo> a repository's rules, for anybody\n" +
2842
+ " settings <repo> [rules…] change the rules (owner)\n" +
2843
+ " builds <repo> game builds allowed to read it (owner)\n" +
2844
+ " publish <folder|zip> --repo <repo> upload a mod; waits for its checks\n" +
2845
+ " list <repo> [--all] your mods and how their checks went\n" +
2846
+ " remove <mod> --repo <repo> take a mod down\n" +
2847
+ " block <username> --repo <repo> stop an uploader (owner)\n" +
2848
+ " reports <repo> what players reported (owner)\n" +
2849
+ " delete <repo> delete a repository and everything in it",
2850
+ options: [
2851
+ { flags: "--name <text>", description: "create/settings: the game's name" },
2852
+ { flags: "--allow <exts>", description: "create/settings: file types a mod may hold, e.g. .png,.json" },
2853
+ { flags: "--nested <exts>", description: "create/settings: archives inside a mod that are opened and checked too" },
2854
+ { flags: "--manifest <file|none>", description: "create/settings: the manifest every part carries (default mod.json)" },
2855
+ { flags: "--fields <list>", description: "create/settings: manifest fields that must match: id,name,folder,version" },
2856
+ { flags: "--uploads <anyone|owner>", description: "create/settings: who may upload" },
2857
+ { flags: "--max-part <size>", description: "create/settings: largest part, e.g. 2GB" },
2858
+ { flags: "--register <file>", description: "builds: register the build secret in this file (never printed)" },
2859
+ { flags: "--new", description: "builds: have the service make a build secret (shown once)" },
2860
+ { flags: "--off <id>", description: "builds: turn a build off" },
2861
+ { flags: "--repo <repo>", description: "publish/list/remove/block: which repository" },
2862
+ { flags: "--part <id>", description: "publish: which part of the mod this is (default main)" },
2863
+ { flags: "--id <mod>", description: "publish: the mod's id (default: manifest, then folder name)" },
2864
+ { flags: "--version <v>", description: "publish: the mod's version" },
2865
+ { flags: "--folder <name>", description: "publish: the folder the game installs it into" },
2866
+ { flags: "--part-name <text>", description: "publish: a readable name for the part" },
2867
+ { flags: "--about <text>", description: "create/settings/publish: a description" },
2868
+ { flags: "--notice <text>", description: "publish: a notice shown with the mod (credits, licences)" },
2869
+ { flags: "--block <exts>", description: "create/settings: more file types to refuse" },
2870
+ { flags: "--max-files <n>", description: "create/settings: most files in a part" },
2871
+ { flags: "--max-ratio <n>", description: "create/settings: how many times its size a part may unpack to" },
2872
+ { flags: "--max-parts <n>", description: "create/settings: most parts in a mod" },
2873
+ { flags: "--expires <date>", description: "builds: when a registered build stops working" },
2874
+ { flags: "--all", description: "list: every mod in the repository (owner)" },
2875
+ { flags: "--as-owner", description: "remove: as the repository's owner, any mod" },
2876
+ { flags: "--disable", description: "settings: stop serving and taking uploads" },
2877
+ { flags: "--enable", description: "settings: start again" },
2878
+ { flags: "--undo", description: "block: let the uploader back in" },
2879
+ { flags: "-y, --yes", description: "remove/delete: without asking" },
2880
+ { flags: "--no-wait", description: "publish: do not wait for the checks" },
2881
+ { flags: "--take-down", description: "block: also hide the uploader's mods" },
2882
+ ],
2883
+ examples: [
2884
+ 'cbx mods create my-game --name "My Game" --allow .png,.ogg,.json --nested .pak',
2885
+ "cbx mods builds my-game --register release/build-secret.bin",
2886
+ "cbx mods publish ./mods/BetterTrees --repo my-game",
2887
+ "cbx mods list my-game",
2888
+ "cbx mods publish ./HD-Art --repo zt1 --part animals",
2889
+ ],
2890
+ run: mods_command_js_1.commandMods,
2891
+ },
2795
2892
  {
2796
2893
  name: "issues",
2797
2894
  group: "Your projects",