@maka/maka-cli 5.65.2 → 5.67.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.
Files changed (95) hide show
  1. package/bundle/typescript/package.json +1 -1
  2. package/bundle/typescript/src/commands/game/_index.d.ts +1 -0
  3. package/bundle/typescript/src/commands/game/_index.js +1 -0
  4. package/bundle/typescript/src/commands/game/_index.js.map +1 -1
  5. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.d.ts +124 -0
  6. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js +487 -29
  7. package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js.map +1 -1
  8. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.d.ts +15 -1
  9. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js +61 -4
  10. package/bundle/typescript/src/commands/game/sideQuest/commands/bluff.js.map +1 -1
  11. package/bundle/typescript/src/commands/game/sideQuest/commands/crew.js +4 -1
  12. package/bundle/typescript/src/commands/game/sideQuest/commands/crew.js.map +1 -1
  13. package/bundle/typescript/src/commands/game/sideQuest/commands/note.d.ts +73 -14
  14. package/bundle/typescript/src/commands/game/sideQuest/commands/note.js +150 -40
  15. package/bundle/typescript/src/commands/game/sideQuest/commands/note.js.map +1 -1
  16. package/bundle/typescript/src/commands/game/sideQuest/commands/review.d.ts +39 -0
  17. package/bundle/typescript/src/commands/game/sideQuest/commands/review.js +48 -0
  18. package/bundle/typescript/src/commands/game/sideQuest/commands/review.js.map +1 -0
  19. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +4 -1
  20. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  21. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  22. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +15 -1
  23. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  24. package/bundle/typescript/src/commands/game/sideQuest/factories/repro-scene.d.ts +87 -0
  25. package/bundle/typescript/src/commands/game/sideQuest/factories/repro-scene.js +320 -0
  26. package/bundle/typescript/src/commands/game/sideQuest/factories/repro-scene.js.map +1 -0
  27. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.d.ts +36 -0
  28. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +116 -3
  29. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +8 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js.map +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +48 -0
  33. package/bundle/typescript/src/commands/game/sideQuest/game.js +112 -13
  34. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  35. package/bundle/typescript/src/commands/game/sideQuest/headless-harness.d.ts +12 -0
  36. package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js +23 -0
  37. package/bundle/typescript/src/commands/game/sideQuest/headless-harness.js.map +1 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/headless.js +4 -1
  39. package/bundle/typescript/src/commands/game/sideQuest/headless.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/models/npc.d.ts +32 -0
  41. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +122 -3
  42. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js.map +1 -1
  43. package/bundle/typescript/src/commands/game/sideQuest/types/repro.d.ts +106 -0
  44. package/bundle/typescript/src/commands/game/sideQuest/types/repro.js +2 -0
  45. package/bundle/typescript/src/commands/game/sideQuest/types/repro.js.map +1 -0
  46. package/bundle/typescript/src/commands/game/sideQuest/types/seed/device-seed.d.ts +12 -0
  47. package/bundle/typescript/src/commands/game/sideQuest/ui.d.ts +9 -0
  48. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  49. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-dump.d.ts +17 -1
  50. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-dump.js +250 -36
  51. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-dump.js.map +1 -1
  52. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.d.ts +67 -1
  53. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js +99 -0
  54. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-review.js.map +1 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-vocab.d.ts +85 -0
  56. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-vocab.js +100 -0
  57. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog-vocab.js.map +1 -0
  58. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.d.ts +58 -3
  59. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js +82 -3
  60. package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js.map +1 -1
  61. package/bundle/typescript/src/commands/game/sideQuest/utilities/commit-ref.d.ts +13 -0
  62. package/bundle/typescript/src/commands/game/sideQuest/utilities/commit-ref.js +22 -0
  63. package/bundle/typescript/src/commands/game/sideQuest/utilities/commit-ref.js.map +1 -0
  64. package/bundle/typescript/src/commands/game/sideQuest/utilities/handoff.d.ts +42 -0
  65. package/bundle/typescript/src/commands/game/sideQuest/utilities/handoff.js +57 -0
  66. package/bundle/typescript/src/commands/game/sideQuest/utilities/handoff.js.map +1 -0
  67. package/bundle/typescript/src/commands/game/sideQuest/utilities/identity.d.ts +36 -0
  68. package/bundle/typescript/src/commands/game/sideQuest/utilities/identity.js +48 -0
  69. package/bundle/typescript/src/commands/game/sideQuest/utilities/identity.js.map +1 -0
  70. package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-launch.d.ts +68 -0
  71. package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-launch.js +147 -0
  72. package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-launch.js.map +1 -0
  73. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js +55 -1
  74. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-grid.js.map +1 -1
  75. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.d.ts +27 -1
  76. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js +44 -11
  77. package/bundle/typescript/src/commands/game/sideQuest/utilities/room-view.js.map +1 -1
  78. package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.d.ts +47 -2
  79. package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.js +76 -8
  80. package/bundle/typescript/src/commands/game/sideQuest/utilities/shared-run.js.map +1 -1
  81. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js +39 -0
  82. package/bundle/typescript/src/commands/game/sideQuest/utilities/spots.js.map +1 -1
  83. package/bundle/typescript/src/commands/game/sideQuest/utilities/table-notes.d.ts +30 -0
  84. package/bundle/typescript/src/commands/game/sideQuest/utilities/table-notes.js +46 -0
  85. package/bundle/typescript/src/commands/game/sideQuest/utilities/table-notes.js.map +1 -0
  86. package/bundle/typescript/src/commands/game/sideQuest-backlog.sub.cmd.d.ts +1 -0
  87. package/bundle/typescript/src/commands/game/sideQuest-backlog.sub.cmd.js +46 -0
  88. package/bundle/typescript/src/commands/game/sideQuest-backlog.sub.cmd.js.map +1 -0
  89. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.d.ts +3 -1
  90. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +269 -42
  91. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  92. package/bundle/typescript/src/tools/log/log.class.d.ts +17 -0
  93. package/bundle/typescript/src/tools/log/log.class.js +30 -1
  94. package/bundle/typescript/src/tools/log/log.class.js.map +1 -1
  95. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import blessed from 'blessed';
2
+ import { nameThem } from './utilities/identity.js';
2
3
  import { dirname } from 'path';
3
4
  import { capitalCase } from 'change-case';
4
5
  import { Category, Size, Rating } from './types/shared/item-enum.js';
@@ -14,7 +15,7 @@ import { isFence, LIFESTYLE_TIERS } from './utilities/commerce.js';
14
15
  import { bestFakeSin, wandRead, processArrest } from './utilities/sin.js';
15
16
  import { renderMapViewport } from './utilities/map-view.js';
16
17
  import { tallestRoomRows } from './utilities/room-grid.js';
17
- import { renderRoomLayoutCompact } from './utilities/room-view.js';
18
+ import { renderRoomLayoutCompact, crewOfTable } from './utilities/room-view.js';
18
19
  import { getPref } from './utilities/prefs.js';
19
20
  import { rollPool, formatRoll } from './utilities/dice.js';
20
21
  import { Direction } from './types/shared/direction-enum.js';
@@ -31,6 +32,7 @@ import { BacklogCommand } from './commands/backlog.js';
31
32
  // help index offers, never what the verb accepts (item D).
32
33
  import { isPlaytester, probePlaytesterStanding } from './utilities/playtester.js';
33
34
  import { RolesCommand } from './commands/roles.js';
35
+ import { ReviewCommand } from './commands/review.js';
34
36
  import { SinCommand } from './commands/sin.js';
35
37
  import { GoCommand } from './commands/go.js';
36
38
  import { UseCommand } from './commands/use.js';
@@ -185,6 +187,9 @@ export default class Game {
185
187
  // social layer stays dark (presence, cloud mirror, street, drafts).
186
188
  _owner;
187
189
  _anonymous = false;
190
+ _bench = false;
191
+ _reproItemId;
192
+ _closing = false;
188
193
  _logFilePath = '';
189
194
  _tier = 1;
190
195
  /**
@@ -253,6 +258,66 @@ export default class Game {
253
258
  get isAnonymous() {
254
259
  return this._anonymous;
255
260
  }
261
+ /**
262
+ * A REPRO BENCH: a scratch session stood up to reproduce one backlog
263
+ * item. Not a life -- no save, no street presence, no shared table.
264
+ *
265
+ * SEPARATE FROM `isAnonymous`, and the separation is the point. The
266
+ * bench first reused the anonymous flag, since both want the street
267
+ * left alone -- and that quietly took the BACKLOG with it, because
268
+ * anonymity is a promise about a RUNNER ("nothing about them leaves
269
+ * this machine") and the backlog is one of the things it forbids. So
270
+ * a play-tester standing in the bench built for their own report was
271
+ * refused when they tried to answer it, which is the one thing the
272
+ * bench exists for.
273
+ *
274
+ * Two different concerns wearing one flag: "this character is
275
+ * private" and "this session is scratch". A bench is not private --
276
+ * it is the opposite, a place to file and confirm from.
277
+ */
278
+ get isBench() {
279
+ return this._bench;
280
+ }
281
+ /** The backlog item this bench was stood up for, if any.
282
+ *
283
+ * The review commands read it so an answer needs no index. Player
284
+ * ruling 2026-08-28: "I shouldn't have to pick from the list of
285
+ * review items -- I AM reviewing an item." Making someone name which
286
+ * report they mean, while they are standing in the scene built for
287
+ * that report, is asking a question the session already answered. */
288
+ get reproItemId() {
289
+ return this._reproItemId;
290
+ }
291
+ /**
292
+ * CLOSE THE SESSION SHORTLY, leaving the last message on screen.
293
+ *
294
+ * For a bench that has just been answered (player request 2026-08-28:
295
+ * "after accepting or rejecting, give a 3s timeout and then close the
296
+ * session"). The pause is the point -- exiting the instant the post
297
+ * lands would wipe the confirmation before it could be read, and the
298
+ * confirmation is the only proof the answer went anywhere.
299
+ *
300
+ * Exits code 0, which is the same door `quit` goes through: the save
301
+ * seal in ui.ts fires (a no-op for a bench, which has no save path)
302
+ * and the launcher parent, which is the terminal's guaranteed
303
+ * janitor, restores the terminal exactly as it would after any other
304
+ * exit. No second teardown path.
305
+ *
306
+ * Idempotent: the first request wins, so answering twice in three
307
+ * seconds cannot stack timers.
308
+ */
309
+ closeAfter(ms, reason) {
310
+ if (this._closing)
311
+ return;
312
+ this._closing = true;
313
+ const timer = setTimeout(() => {
314
+ Logger.getInstance().write(`<<<<<<<<--------- Closing session: ${reason}`);
315
+ process.exit(0);
316
+ }, ms);
317
+ // Never hold the process open on its own account -- if everything
318
+ // else has finished, going now is right.
319
+ timer.unref?.();
320
+ }
256
321
  get sessionDir() {
257
322
  return this._logFilePath ? dirname(this._logFilePath) : undefined;
258
323
  }
@@ -311,7 +376,11 @@ export default class Game {
311
376
  livePartyFingerprint() {
312
377
  return this.crew
313
378
  .filter(m => m.ghost?.liveOnly)
314
- .map(m => m.ghost.owner)
379
+ // BY CHARACTER, not by account (see HubLink.seatHolds): two
380
+ // runners on one login are a legitimate pair, and keying this on
381
+ // the account made a swap between them invisible -- the quote
382
+ // would have survived a crew change it could not see.
383
+ .map(m => m.ghost.slug || m.ghost.owner)
315
384
  .sort()
316
385
  .join(',');
317
386
  }
@@ -447,13 +516,13 @@ export default class Game {
447
516
  * push one right now and say so.
448
517
  */
449
518
  async pushPresenceNow() {
450
- if (this._anonymous)
519
+ if (this._anonymous || this._bench)
451
520
  return 'offline';
452
521
  return heartbeatPresenceNow(this.presencePayload());
453
522
  }
454
523
  startPresenceHeartbeat() {
455
- if (this._anonymous)
456
- return; // account-less: never on the street
524
+ if (this._anonymous || this._bench)
525
+ return; // account-less, or a bench: never on the street
457
526
  if (this._presenceTimer)
458
527
  return;
459
528
  this.beatPresence();
@@ -1507,8 +1576,8 @@ export default class Game {
1507
1576
  /** The transport, hooks-first, CLI default second; undefined =
1508
1577
  * offline/logged out/classic mode (callers degrade quietly). */
1509
1578
  async streetTransport() {
1510
- if (this._anonymous)
1511
- return undefined; // account-less: no street
1579
+ if (this._anonymous || this._bench)
1580
+ return undefined; // account-less, or a bench: no street
1512
1581
  if (this.listStreetPlayers && this.sendPage) {
1513
1582
  return { list: this.listStreetPlayers, send: this.sendPage };
1514
1583
  }
@@ -1800,6 +1869,8 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
1800
1869
  CommandFactory.registerCommand('note', NoteCommand);
1801
1870
  CommandFactory.registerCommand('backlog', BacklogCommand);
1802
1871
  CommandFactory.registerCommand('roles', RolesCommand);
1872
+ // The short form of "backlog review" -- see commands/review.ts.
1873
+ CommandFactory.registerCommand('review', ReviewCommand);
1803
1874
  CommandFactory.registerCommand('sin', SinCommand);
1804
1875
  CommandFactory.registerCommand('sins', SinCommand);
1805
1876
  CommandFactory.registerCommand('papers', SinCommand);
@@ -2073,6 +2144,8 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2073
2144
  this._saveFilePath = options.saveFilePath;
2074
2145
  this._owner = options.owner;
2075
2146
  this._anonymous = options.anonymousRunner === true;
2147
+ this._bench = options.reproBench === true;
2148
+ this._reproItemId = options.reproItemId;
2076
2149
  // Ask the server, once, whether this account is a play-tester, so
2077
2150
  // the help index can stop advertising a backlog that will only 403
2078
2151
  // (item D). Fire-and-forget on purpose: it never blocks the boot,
@@ -2125,7 +2198,23 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2125
2198
  // spawned in a phantom hideout and read it as a kick back to the
2126
2199
  // hub). The room object still exists for any code that touches
2127
2200
  // _homeRoom; it's just not in the scene.
2128
- if (!options.sharedRun) {
2201
+ // A BENCH GETS NO HIDEOUT EITHER, for the same reason a shared run
2202
+ // does not: nobody lives here. It is two rooms and a door.
2203
+ //
2204
+ // FOUND FROM A LIVE SESSION LOG (2026-08-28). The bench promises
2205
+ // "you start standing at the barrier", and it did not: the hideout
2206
+ // was added and the player was moved INTO it, so the first thing a
2207
+ // reviewer had to do was "go up" out of a home the bench had built
2208
+ // them. They reported the steps as wrong; the steps were right and
2209
+ // the scene had grown a room.
2210
+ //
2211
+ // WORTH RECORDING WHY THE DRY RUN MISSED IT. The headless harness
2212
+ // boots with sharedRun: true, so it took the branch that skips all
2213
+ // of this -- the replay I had been calling proof of the bench was
2214
+ // quietly proving a DIFFERENT scene from the one a player gets.
2215
+ // Adding reproBench here is what makes the two agree, which is the
2216
+ // property the dry run was supposed to have all along.
2217
+ if (!options.sharedRun && !options.reproBench) {
2129
2218
  this.scene.addRoom(this._homeRoom);
2130
2219
  this.connectHomeRoom(this.player.currentLocation);
2131
2220
  // Every session starts AT HOME -- the hideout, one door from the
@@ -2791,7 +2880,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2791
2880
  // offer flow (his call, accept/decline, the taxi) works unchanged.
2792
2881
  // Offline owners fall back to the Phase B ghost riders below.
2793
2882
  let sharedDeclineNote = '';
2794
- if (this.hasHub && !this._anonymous) {
2883
+ if (this.hasHub && !this._anonymous && !this._bench) {
2795
2884
  const srMod = await import('./utilities/shared-run.js').catch(() => undefined);
2796
2885
  const preferred = this.preferredDraftSessionID;
2797
2886
  this.preferredDraftSessionID = undefined;
@@ -2843,17 +2932,26 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
2843
2932
  // Off-street seat-mates were dropped from tonight's quote by the
2844
2933
  // server (their seats survive -- back on the street, back on the
2845
2934
  // job). Krow NAMES them; silence here already cost one playtest.
2846
- const nameOf = (h) => this.crew.find(m => m.ghost?.owner === h)?.name ?? h;
2847
- const absentNames = (shared.absent ?? []).map(nameOf);
2935
+ // Krow speaks RUNNER names. He used to fall back to the raw
2936
+ // account handle when a seat wasn't on the local roster, so
2937
+ // the fiction said "couldn't raise doubleglizzy" out loud.
2938
+ // Anyone we can't name is counted instead (identity.ts).
2939
+ const nameOf = (h) => this.crew.find(m => m.ghost?.owner === h)?.name;
2940
+ const absentNames = nameThem(shared.absent ?? [], nameOf);
2848
2941
  const absentNote = absentNames.length
2849
2942
  ? ` "Couldn't raise ${absentNames.join(', ')} -- quoted for who answered."`
2850
2943
  : '';
2851
2944
  // Krow refuses unvetted runners BY NAME (fiction ruling
2852
2945
  // 2026-08-24) -- their seat survives; the OOC line tells the
2853
2946
  // table how they get vouched for.
2854
- const unvettedNames = (shared.unvetted ?? []).map(nameOf);
2947
+ const unvettedHandles = shared.unvetted ?? [];
2948
+ const unvettedNames = nameThem(unvettedHandles, nameOf);
2949
+ // The in-character line takes RUNNER names; the out-of-character
2950
+ // one keeps the ACCOUNT handles, because the thing it tells them
2951
+ // to go and fix is the account. Stripping it there would break
2952
+ // the only instruction that helps them (identity.ts).
2855
2953
  const unvettedNote = unvettedNames.length
2856
- ? ` "${unvettedNames.join(', ')}? No vouch on the street, no seat at my table. They square their paperwork, they ride the next one."\n{yellow-fg}(Out of character: ${unvettedNames.join(', ')} needs a verified account email -- \`maka register --resend-verification\` on their machine.){/yellow-fg}`
2954
+ ? ` "${unvettedNames.join(', ')}? No vouch on the street, no seat at my table. They square their paperwork, they ride the next one."\n{yellow-fg}(Out of character: ${unvettedHandles.join(', ')} needs a verified account email -- \`maka register --resend-verification\` on their machine.){/yellow-fg}`
2857
2955
  : '';
2858
2956
  const reusedNote = shared.reused ? ` "Same job as before, chummer -- the client's still waiting."` : '';
2859
2957
  return `Your link pings -- 1 new message: ${FIXER_NAME}.${absentNote}${unvettedNote}${reusedNote} The crew's channel is already humming.${hint(` ("call ${FIXER_NAME}" to hear the job -- "jobs" shows the board.)`)}`;
@@ -3936,6 +4034,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
3936
4034
  // blank box and a log line, not a dead session mid-job.
3937
4035
  try {
3938
4036
  const roomLines = renderRoomLayoutCompact(here, this.player, {
4037
+ isTableAlly: crewOfTable(this.scene.getPlayers()),
3939
4038
  boxWidth,
3940
4039
  isOtherPlayer: a => this.scene.isHumanControlled(a),
3941
4040
  });