@maka/maka-cli 5.226.0 → 5.228.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.
- package/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/disable.js +14 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/look.js +12 -1
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +50 -4
- package/bundle/typescript/src/commands/game/sideQuest/models/room.js +30 -0
- package/bundle/typescript/src/commands/game/sideQuest/types/repro.js +36 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/ar.js +5 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/grid-view.js +81 -8
- package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-boot.js +62 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/repro-launch.js +19 -38
- package/bundle/typescript/src/commands/game/sideQuest/utilities/silent-icons.js +43 -22
- package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +10 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.228.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|
|
@@ -134,7 +134,20 @@ export class DisableCommand extends Command {
|
|
|
134
134
|
return t.viaWan && t.host?.host ? wanDefensePool(t.device, t.host.host) : deviceDefensePool(t.device);
|
|
135
135
|
}
|
|
136
136
|
if (t.kind === 'camera') {
|
|
137
|
-
|
|
137
|
+
// THE LENSES RATE WHAT THE SITE SAYS THEY RATE (Room.cameraRating,
|
|
138
|
+
// eJANPpm7qCaCAJBJw). This used to borrow the host's whole pool
|
|
139
|
+
// while silent-icons.ts hid the same cluster behind a hard-coded 4
|
|
140
|
+
// and the icon line printed no rating at all -- three answers to
|
|
141
|
+
// one question, two of them invisible. A slaved camera still
|
|
142
|
+
// borrows its master where the master is HIGHER (p.233, the same
|
|
143
|
+
// rule wanDefensePool applies to a slaved device), so a corp host
|
|
144
|
+
// still hardens its own cameras; what changed is that the floor is
|
|
145
|
+
// the seeded rating rather than a constant nobody could read.
|
|
146
|
+
const own = { label: 'Device Rating + Firewall', pool: Math.max(0, t.room.cameraRating * 2) };
|
|
147
|
+
if (!t.master?.host)
|
|
148
|
+
return own;
|
|
149
|
+
const viaHost = hostDefensePool(t.master.host);
|
|
150
|
+
return viaHost.pool >= own.pool ? viaHost : own;
|
|
138
151
|
}
|
|
139
152
|
return { label: 'Intuition + Firewall', pool: t.owner.getPanDefensePool('sleaze') };
|
|
140
153
|
}
|
|
@@ -333,7 +333,17 @@ export class LookCommand extends Command {
|
|
|
333
333
|
// in the icon block below rather than one look later.
|
|
334
334
|
const swept = spotHiddenIcons(this.scene, this.actor, this.actor.insideHost === room ? room : gridVicinity(this.actor));
|
|
335
335
|
const lines = [gridSculpt(this.actor, room)];
|
|
336
|
-
|
|
336
|
+
// THE SWEEP IS REPORTED BEFORE THE LIST IT CHANGED
|
|
337
|
+
// (eJANPpm7qCaCAJBJw, third rejection: "I still see the Service
|
|
338
|
+
// Keypad and Camera cluster").
|
|
339
|
+
//
|
|
340
|
+
// The sweep runs first so anything it wins appears in this look
|
|
341
|
+
// rather than one look later -- that part was right. But its
|
|
342
|
+
// SENTENCE was pushed after the icon block, so the player read a
|
|
343
|
+
// newly-revealed keypad with nothing above it saying why, and the
|
|
344
|
+
// "you pick an icon out of the noise" line arrived two blocks down.
|
|
345
|
+
// Revealing something and explaining it in that order reads exactly
|
|
346
|
+
// like never having hidden it, which is what was reported.
|
|
337
347
|
if (swept) {
|
|
338
348
|
lines.push(spotLine(swept));
|
|
339
349
|
if (showsMechanics(this.scene, this.actor)) {
|
|
@@ -342,6 +352,7 @@ export class LookCommand extends Command {
|
|
|
342
352
|
}
|
|
343
353
|
}
|
|
344
354
|
}
|
|
355
|
+
lines.push(gridIconBlock(this.scene, this.actor, room, this.rooms, swept?.found));
|
|
345
356
|
// THE COACHING HAS TO MATCH THE ROOM YOU ARE IN. Inside a host,
|
|
346
357
|
// `go` is refused (go.ts checkHostBoundary) and `map` draws the
|
|
347
358
|
// outside grid you cannot reach -- so offering both is the same
|
|
@@ -1251,9 +1251,13 @@
|
|
|
1251
1251
|
// 'panel' -- the maglock, the keypad, the safe). A ward is the Art
|
|
1252
1252
|
// and has no wireless mode to hide in.
|
|
1253
1253
|
// - CAMERAS TOO, which p.217 names FIRST. The cluster is not a Device
|
|
1254
|
-
// (it is derived from the room), so it hides on Room.camerasSpottedBy
|
|
1255
|
-
//
|
|
1256
|
-
//
|
|
1254
|
+
// (it is derived from the room), so it hides on Room.camerasSpottedBy.
|
|
1255
|
+
// (This entry ORIGINALLY said it resisted with its MASTER HOST'S
|
|
1256
|
+
// rating. That was wrong and 1.80.1 removed it the same day -- a
|
|
1257
|
+
// lens behind a rating-8 host was a 16-die hide against a sweep
|
|
1258
|
+
// capped near 5, and it measured ZERO finds in twelve looks. It
|
|
1259
|
+
// resists with its own rating; see 1.82.0 for where that number
|
|
1260
|
+
// now lives.)
|
|
1257
1261
|
// - AND PEOPLE. Going dark used to make an icon simply unfindable:
|
|
1258
1262
|
// isLinked answers false and nothing anywhere rolled to beat it, so
|
|
1259
1263
|
// the book's own example of a hidden icon was the one case with no
|
|
@@ -1325,5 +1329,47 @@
|
|
|
1325
1329
|
// hole had been open: they leaned on the old "no gear, stay quiet"
|
|
1326
1330
|
// branch and have been rolling unlimited all along. They carry decks
|
|
1327
1331
|
// now. The VR gate (a direct neural interface, p.222-223) is untouched.
|
|
1328
|
-
|
|
1332
|
+
// 1.82.0 (2026-09-16): WHAT MATRIX PERCEPTION BUYS MUST BE EARNED
|
|
1333
|
+
// (eJANPpm7qCaCAJBJw, third rejection). 1.80.0 hid the ICON and stopped
|
|
1334
|
+
// there. The reporter read the transcript and found the rest:
|
|
1335
|
+
// "I shouldn't even see 'Service Keypad hides' at all if I don't
|
|
1336
|
+
// clear the firewall."
|
|
1337
|
+
// They were right, and the leak was in the DICE, not the prose. The
|
|
1338
|
+
// narrative line was correctly vague -- "something here is running
|
|
1339
|
+
// silent and you cannot pin it" -- and then the mechanics pane named
|
|
1340
|
+
// the thing on the very next line, win or lose:
|
|
1341
|
+
// Service Keypad hides (Device Rating (no Logic to hide behind)): 6d6 -> 2 hits
|
|
1342
|
+
// A FAILED SWEEP TAUGHT YOU MORE THAN A SUCCESSFUL ONE WOULD HAVE.
|
|
1343
|
+
// - A LOST SWEEP NAMES NOTHING. ISpotOutcome.missed is now a COUNT,
|
|
1344
|
+
// not a list of names, and the losing branch of the loop never puts
|
|
1345
|
+
// a name in the outcome at all. This is the shape of the fix that
|
|
1346
|
+
// matters: the old type carried the names and merely trusted every
|
|
1347
|
+
// caller to remember not to print them. A name that was never earned
|
|
1348
|
+
// cannot now be printed by accident, because it is not there. The
|
|
1349
|
+
// dice still show: "something holds its cover: 6d6 -> 2 hits".
|
|
1350
|
+
// - RATINGS ARE PROTECTED TOO, which is the reporter's other question
|
|
1351
|
+
// answered. p.235 sells "the target's device rating" for a hit, and
|
|
1352
|
+
// the engine printed [DR n] on every icon for free. It is now
|
|
1353
|
+
// [DR ?] until the icon is yours: ratingKnownBy = not silent, or
|
|
1354
|
+
// spotted by you. NOTE THE SMALLER CLAIM -- a never-silent device
|
|
1355
|
+
// shows its rating for nothing. p.235 charges a hit for it even
|
|
1356
|
+
// then; this engine does not, because a loud icon has nothing to
|
|
1357
|
+
// roll against and inventing a resistance for it would be inventing
|
|
1358
|
+
// a rule. Said plainly rather than papered over.
|
|
1359
|
+
// - THE CAMERA CLUSTER HAS A RATING AT LAST, which was the third
|
|
1360
|
+
// complaint. Room.cameraRating, ONE number, feeding all three places
|
|
1361
|
+
// that disagreed: what it hides behind (Device Rating + Firewall),
|
|
1362
|
+
// what it defends Control Device / Data Spike with, and what the
|
|
1363
|
+
// grid prints. It is 1 (p.356 lists Camera at 1; the same list gives
|
|
1364
|
+
// the door lock the 2 the engine already uses). It is a FIELD, so a
|
|
1365
|
+
// corp floor that wants p.421's mil-spec band is a one-line change
|
|
1366
|
+
// in the seed layer, not a rewrite.
|
|
1367
|
+
// - AND THE SWEEP READS AS A SWEEP: the narration moved ABOVE the icon
|
|
1368
|
+
// block (you were reading revealed icons before the sentence that
|
|
1369
|
+
// explained them), and an icon found this look is tagged "<- just
|
|
1370
|
+
// spotted, was running silent". Without the tag, every later look
|
|
1371
|
+
// shows it with no explanation at all -- indistinguishable from an
|
|
1372
|
+
// icon that was never hidden, which is most of why 1.80.0 read to
|
|
1373
|
+
// the reporter as nothing having happened.
|
|
1374
|
+
export const ENGINE_VERSION = '1.82.0';
|
|
1329
1375
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -12,6 +12,13 @@ import { AbstractRoom } from '../types/shared/abstracts.js';
|
|
|
12
12
|
import { spotsActive, actorDistanceMeters, voiceBandFor, voiceTag, doorwayKindOf, exitDoorwayKind } from '../utilities/spots.js';
|
|
13
13
|
import { canHearSpeech, canHearShout } from '../utilities/earshot.js';
|
|
14
14
|
import { synthesizeGrid, attachExitCell, detachExitCell, } from '../utilities/room-grid.js';
|
|
15
|
+
/**
|
|
16
|
+
* A CAMERA'S DEVICE RATING when the scene does not say (p.356, which
|
|
17
|
+
* lists "Camera" at 1 -- the same table that gives the door lock the 2
|
|
18
|
+
* DEFAULT_DEVICE_RATING already uses). A seed raises it: p.421 bands
|
|
19
|
+
* ordinary security devices at 2 and mil-spec at 4.
|
|
20
|
+
*/
|
|
21
|
+
export const DEFAULT_CAMERA_RATING = 1;
|
|
15
22
|
export class Room extends AbstractRoom {
|
|
16
23
|
logger = Logger.getInstance();
|
|
17
24
|
actors = new Set();
|
|
@@ -74,6 +81,29 @@ export class Room extends AbstractRoom {
|
|
|
74
81
|
* of the grid.
|
|
75
82
|
*/
|
|
76
83
|
camerasSpottedBy = new Set();
|
|
84
|
+
/**
|
|
85
|
+
* WHAT THE LENSES RATE (eJANPpm7qCaCAJBJw, third rejection: "the
|
|
86
|
+
* camera cluster doesn't have a DR"), and the reporter's ruling that
|
|
87
|
+
* it should be per-site rather than one global number.
|
|
88
|
+
*
|
|
89
|
+
* The cluster is not a Device -- it is derived from the room itself
|
|
90
|
+
* (utilities/surveillance.ts isWatched) -- so it had no rating to
|
|
91
|
+
* carry, and the codebase had quietly grown THREE answers that
|
|
92
|
+
* disagreed: it hid behind a hard-coded 4, it defended a Data Spike
|
|
93
|
+
* with the HOST's pool (commands/disable.ts), and it displayed no
|
|
94
|
+
* rating at all. One number now feeds all three, so what a player
|
|
95
|
+
* reads is what it rolls.
|
|
96
|
+
*
|
|
97
|
+
* ONE FOR NOW (user ruling 2026-09-16), which is what p.356 lists a
|
|
98
|
+
* Camera at -- the same table the engine already takes its door-lock 2
|
|
99
|
+
* from. Per-site seeding was scoped out deliberately: the defect here
|
|
100
|
+
* was three numbers disagreeing, and one field fixes that without a
|
|
101
|
+
* seed knob the generator could get wrong. It is a FIELD rather than a
|
|
102
|
+
* constant so that making it per-site later is a one-line change at
|
|
103
|
+
* the seed layer and nothing else moves. p.421 bands ordinary security
|
|
104
|
+
* devices at 2 and mil-spec at 4 when that day comes.
|
|
105
|
+
*/
|
|
106
|
+
cameraRating = DEFAULT_CAMERA_RATING;
|
|
77
107
|
/**
|
|
78
108
|
* THE FALLEN (player ruling 2026-08-25). A killed actor leaves a BODY
|
|
79
109
|
* where they dropped, holding everything they carried, instead of
|
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE BENCH'S ACTOR. One name, used for the seat, for the transcript's
|
|
3
|
+
* visibility filter, and for the post-boot fixture state below.
|
|
4
|
+
*
|
|
5
|
+
* IT LIVES HERE, in the types module, on purpose. utilities/repro-launch
|
|
6
|
+
* declared it, and the launcher's whole design is that the game graph
|
|
7
|
+
* (repro-scene, the harness, blessed, 150 files) stays behind a dynamic
|
|
8
|
+
* import so `maka --help` does not pay for it. Once the site needed the
|
|
9
|
+
* same constant, anything that re-exported it from a module importing
|
|
10
|
+
* the builder would have dragged that graph back across the lazy
|
|
11
|
+
* boundary. This file imports nothing, so both sides can share the name
|
|
12
|
+
* for free.
|
|
13
|
+
*/
|
|
14
|
+
export const REPRO_ACTOR = 'Tester';
|
|
15
|
+
/**
|
|
16
|
+
* THE FIXTURE A BENCH ACTUALLY BUILDS FROM.
|
|
17
|
+
*
|
|
18
|
+
* One rule, and it is not a convenience: a bench written with
|
|
19
|
+
* `repro.premise` and no `fixture.premise` is the common shape, so the
|
|
20
|
+
* reader's framing doubles as the scene's story when the fixture has
|
|
21
|
+
* none of its own. Three callers build a bench -- the headless dry run,
|
|
22
|
+
* the CLI's live launcher, and maka-cli.com's web bench -- and each had
|
|
23
|
+
* to know this. Two of them spelling it the same way and a third
|
|
24
|
+
* forgetting is how a bench acquires a different opening line depending
|
|
25
|
+
* on where it was run from.
|
|
26
|
+
*
|
|
27
|
+
* Here rather than beside the builder because this file imports nothing,
|
|
28
|
+
* so the launcher can apply the rule without reaching across its lazy
|
|
29
|
+
* boundary into the game graph.
|
|
30
|
+
*/
|
|
31
|
+
export function reproFixtureOf(repro) {
|
|
32
|
+
const fixture = { ...(repro.fixture ?? {}) };
|
|
33
|
+
if (!fixture.premise && repro.premise)
|
|
34
|
+
fixture.premise = repro.premise;
|
|
35
|
+
return fixture;
|
|
36
|
+
}
|
|
1
37
|
/**
|
|
2
38
|
* THE FIXTURE SCHEMA'S VERSION, and the engine side is the source of
|
|
3
39
|
* truth for it. Anything that GENERATES repro drafts -- the
|
|
@@ -2,6 +2,7 @@ import { isLinked, CALL_COLOR } from './comm-style.js';
|
|
|
2
2
|
import { isWatched, camerasLive, canSnoopFeeds } from './surveillance.js';
|
|
3
3
|
import { Category } from '../types/shared/item-enum.js';
|
|
4
4
|
import { hint } from './hints.js';
|
|
5
|
+
import { cameraDrTag, drTagFor } from './grid-view.js';
|
|
5
6
|
/**
|
|
6
7
|
* AROs -- AUGMENTED REALITY OBJECTS (player request 2026-08-26: "we
|
|
7
8
|
* need AROs on anything online"). The 2075 street is not bare: anything
|
|
@@ -147,9 +148,10 @@ export function aroTags(scene, actor, room) {
|
|
|
147
148
|
// The verb rides the tag: standing in the room IS the requirement
|
|
148
149
|
// for a direct connection (commands/tap.ts), so this is the one
|
|
149
150
|
// place the player is always in a position to use it.
|
|
151
|
+
const camDr = cameraDrTag(room, actor.name);
|
|
150
152
|
tags.push(canSnoopFeeds(rooms, room, actor) && !camerasLive(rooms, room)
|
|
151
|
-
? `◎ Camera cluster -- wireless, and looping empty hallways for the house.`
|
|
152
|
-
: `◎ Camera cluster -- wireless, and watching.${hint(` ("tap" cables into it -- a slaved lens can't hide behind its host)`)}`);
|
|
153
|
+
? `◎ Camera cluster ${camDr}-- wireless, and looping empty hallways for the house.`
|
|
154
|
+
: `◎ Camera cluster ${camDr}-- wireless, and watching.${hint(` ("tap" cables into it -- a slaved lens can't hide behind its host)`)}`);
|
|
153
155
|
}
|
|
154
156
|
// THE FIXED DEVICES -- the keypad on the wall, the maglock on the
|
|
155
157
|
// grate (YjBA9xhY2NDmCFw5d, Deditri: "The keypad should be
|
|
@@ -174,7 +176,7 @@ export function aroTags(scene, actor, room) {
|
|
|
174
176
|
// still picked, breached, shot and keyed exactly as before.
|
|
175
177
|
if (!device.hasIconFor(actor.name))
|
|
176
178
|
continue;
|
|
177
|
-
tags.push(`▤ ${device.name} -- wireless,
|
|
179
|
+
tags.push(`▤ ${device.name} -- wireless, ${drTagFor(device, actor.name)}${device.isOpen() ? ', standing open' : `.${hint(` ("${device.verbs()[0]} ${device.name}")`)}`}`);
|
|
178
180
|
}
|
|
179
181
|
// LOOSE DEVICES lying in plain sight. A wireless deck on a table
|
|
180
182
|
// announces itself; one in a drawer you never opened does not.
|
|
@@ -23,6 +23,65 @@ const NEAR_DIVIDER = '-- and NEAR it, out on the open grid (not inside the host)
|
|
|
23
23
|
export function isNearDivider(line) {
|
|
24
24
|
return line === NEAR_DIVIDER;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* A DEVICE RATING IS SOMETHING YOU BUY WITH A HIT, not something the
|
|
28
|
+
* screen gives away (eJANPpm7qCaCAJBJw -- "are device ratings
|
|
29
|
+
* themselves, protected information?"; p.235).
|
|
30
|
+
*
|
|
31
|
+
* p.235 lists what one hit on a Matrix Perception test buys, and "the
|
|
32
|
+
* target's device rating" is an entry on it, beside "the type of icon"
|
|
33
|
+
* and "the rating of one of the target's Matrix attributes". The engine
|
|
34
|
+
* printed [DR n] on every icon for nothing, which is the same defect the
|
|
35
|
+
* silent-icon work fixed one field over: the rating was free.
|
|
36
|
+
*
|
|
37
|
+
* WHAT COUNTS AS HAVING READ IT is the sweep that found the icon
|
|
38
|
+
* (utilities/silent-icons.ts). Winning it buys the icon AND its rating
|
|
39
|
+
* together -- one hit, one look, one fact.
|
|
40
|
+
*
|
|
41
|
+
* AND A LOUD ICON PUBLISHES ITS RATING, which is where this stops short
|
|
42
|
+
* of p.235 on purpose. Strictly the book charges a hit for the rating of
|
|
43
|
+
* ANY icon, loud or not; modelling that needs a per-fact ledger of what
|
|
44
|
+
* each viewer has bought about each icon, which nothing else in this
|
|
45
|
+
* engine keeps and which would put a roll between a player and a number
|
|
46
|
+
* they can already see on the shop window. The first cut gated every
|
|
47
|
+
* rating on `spottedBy` and the consequence was immediate: a device that
|
|
48
|
+
* was never silent is never spotted, so its rating read [DR ?] forever
|
|
49
|
+
* with no way to earn it. So the line is drawn at HIDING -- a rating you
|
|
50
|
+
* were never meant to see is protected, a rating the thing broadcasts is
|
|
51
|
+
* not -- and that is a smaller claim than p.235's, stated here rather
|
|
52
|
+
* than dressed up as the whole rule.
|
|
53
|
+
*/
|
|
54
|
+
export function ratingKnownBy(device, viewerName) {
|
|
55
|
+
return !device.silent || device.spottedBy.has(viewerName);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The bracketed rating, or `[DR ?]` when it has not been read.
|
|
59
|
+
*
|
|
60
|
+
* A QUESTION MARK RATHER THAN NOTHING, on purpose: the icon's SHAPE is
|
|
61
|
+
* free (p.219 -- it looks like the thing it is), so a player should see
|
|
62
|
+
* that this object HAS a rating and that they have not bought it yet.
|
|
63
|
+
* Printing nothing would read as "this one has no rating", which is a
|
|
64
|
+
* different and wrong fact, and is exactly the ambiguity that made the
|
|
65
|
+
* camera cluster's missing DR look like an oversight.
|
|
66
|
+
*/
|
|
67
|
+
export function drTag(known, rating) {
|
|
68
|
+
return known ? `{bold}[DR ${rating}]{/bold}` : `{bold}[DR ?]{/bold}`;
|
|
69
|
+
}
|
|
70
|
+
/** One rule for both surfaces: the grid and the AR overlay ask this, so
|
|
71
|
+
* a rating the street can read and a persona cannot is impossible. */
|
|
72
|
+
export function drTagFor(device, viewerName) {
|
|
73
|
+
return drTag(ratingKnownBy(device, viewerName), device.rating);
|
|
74
|
+
}
|
|
75
|
+
/** The camera cluster's rating tag. Cameras are always security kit and
|
|
76
|
+
* therefore always silent, so unlike a device there is no loud case:
|
|
77
|
+
* reading the lenses is the only way to their number. */
|
|
78
|
+
export function cameraDrTag(room, viewerName) {
|
|
79
|
+
return drTag(room.camerasSpottedBy.has(viewerName), room.cameraRating);
|
|
80
|
+
}
|
|
81
|
+
/** Trailing space for the icon lines that interpolate it mid-sentence. */
|
|
82
|
+
function cameraDr(room, viewerName) {
|
|
83
|
+
return `${cameraDrTag(room, viewerName)} `;
|
|
84
|
+
}
|
|
26
85
|
/**
|
|
27
86
|
* A DEVICE ICON, AND WHAT WORKS IT (commands/disable.ts, 2026-09-13).
|
|
28
87
|
* Control Device (p.238) needs 2 marks on the device -- "disable" -- and
|
|
@@ -30,7 +89,7 @@ export function isNearDivider(line) {
|
|
|
30
89
|
* locked (p.228), so its line says so rather than offering a way in.
|
|
31
90
|
*/
|
|
32
91
|
export function deviceIconLine(device, actor) {
|
|
33
|
-
const dr =
|
|
92
|
+
const dr = drTag(ratingKnownBy(device, actor.name), device.rating);
|
|
34
93
|
if (device.isOpen())
|
|
35
94
|
return `▤ ${device.name} ${dr} -- its icon standing open, nothing left to hold.`;
|
|
36
95
|
if (device.isBricked) {
|
|
@@ -275,10 +334,10 @@ export function gridIcons(scene, actor, room) {
|
|
|
275
334
|
if (isWatched(room) && room.camerasSpottedBy.has(actor.name)) {
|
|
276
335
|
const rooms = scene.getRooms();
|
|
277
336
|
if (!camerasLive(rooms, room)) {
|
|
278
|
-
icons.push(`◎ Camera cluster -- looped on empty hallways. Security sees NOTHING here.${canSnoopFeeds(rooms, room, actor) ? ` ("snoop" rides the feeds you own)` : ''}`);
|
|
337
|
+
icons.push(`◎ Camera cluster ${cameraDr(room, actor.name)}-- looped on empty hallways. Security sees NOTHING here.${canSnoopFeeds(rooms, room, actor) ? ` ("snoop" rides the feeds you own)` : ''}`);
|
|
279
338
|
}
|
|
280
339
|
else if (canSnoopFeeds(rooms, room, actor)) {
|
|
281
|
-
icons.push(`◎ Camera cluster -- live, and answering to YOU as much as the house. ("snoop <room>" watches remotely)`);
|
|
340
|
+
icons.push(`◎ Camera cluster ${cameraDr(room, actor.name)}-- live, and answering to YOU as much as the house. ("snoop <room>" watches remotely)`);
|
|
282
341
|
}
|
|
283
342
|
else {
|
|
284
343
|
// Seen from the grid, the cluster is worth naming as a WAY IN,
|
|
@@ -296,7 +355,7 @@ export function gridIcons(scene, actor, room) {
|
|
|
296
355
|
// exactly one, so the reader was left to guess at a fact the
|
|
297
356
|
// engine had already handed them.
|
|
298
357
|
const owner = cameraMasterHost(scene.getRooms(), room);
|
|
299
|
-
icons.push(`◎ Camera cluster -- live, feeding ${owner ? hostLabel(owner) : `the site's host`}. Loud acts here travel. (own the host to loop them -- or walk in and "tap" a lens in the flesh: slaved to the host means a key to the host)`);
|
|
358
|
+
icons.push(`◎ Camera cluster ${cameraDr(room, actor.name)}-- live, feeding ${owner ? hostLabel(owner) : `the site's host`}. Loud acts here travel. (own the host to loop them -- or walk in and "tap" a lens in the flesh: slaved to the host means a key to the host)`);
|
|
300
359
|
}
|
|
301
360
|
}
|
|
302
361
|
// THE DIVIDER (eJANPpm7qCaCAJBJw). Only when there is a host to be
|
|
@@ -570,14 +629,28 @@ export function gridSculpt(actor, room) {
|
|
|
570
629
|
* the dimmer their icons"). `rooms` absent means the caller has only
|
|
571
630
|
* the one room, and the block reads as it always did.
|
|
572
631
|
*/
|
|
573
|
-
export function gridIconBlock(scene, actor, room, rooms) {
|
|
632
|
+
export function gridIconBlock(scene, actor, room, rooms, justFound) {
|
|
574
633
|
const isHostLine = (i) => i.startsWith('●') || i.startsWith('■');
|
|
634
|
+
// WHAT THIS LOOK JUST TURNED UP, tagged where it is listed
|
|
635
|
+
// (eJANPpm7qCaCAJBJw: "I still see the Service Keypad and Camera
|
|
636
|
+
// cluster"). An icon a sweep has just won is otherwise identical on
|
|
637
|
+
// the page to one that was never hidden -- and on every LATER look
|
|
638
|
+
// there is no sweep line at all, because there is nothing left to
|
|
639
|
+
// roll against, so without this the moment of finding something is
|
|
640
|
+
// invisible and the whole mechanic reads as not having run.
|
|
641
|
+
const mark = (icon) => {
|
|
642
|
+
if (!justFound?.length)
|
|
643
|
+
return icon;
|
|
644
|
+
return justFound.some(n => icon.includes(n))
|
|
645
|
+
? `${icon} {bold}<- just spotted, was running silent{/bold}`
|
|
646
|
+
: icon;
|
|
647
|
+
};
|
|
575
648
|
if (actor.insideHost || !rooms) {
|
|
576
649
|
const icons = gridIcons(scene, actor, room);
|
|
577
650
|
if (icons.length === 0) {
|
|
578
651
|
return `{light-blue-fg}Thin grid out here -- background noise, nothing worth cracking.{/light-blue-fg}`;
|
|
579
652
|
}
|
|
580
|
-
return `{light-blue-fg}ICONS IN REACH:{/light-blue-fg}\n${icons.map(i => ` {light-blue-fg}${i}{/light-blue-fg}`).join('\n')}`;
|
|
653
|
+
return `{light-blue-fg}ICONS IN REACH:{/light-blue-fg}\n${icons.map(i => ` {light-blue-fg}${mark(i)}{/light-blue-fg}`).join('\n')}`;
|
|
581
654
|
}
|
|
582
655
|
const out = [];
|
|
583
656
|
const hosts = hostsInReach(rooms, actor);
|
|
@@ -629,12 +702,12 @@ export function gridIconBlock(scene, actor, room, rooms) {
|
|
|
629
702
|
return line;
|
|
630
703
|
// Box-drawing, so the last child closes the branch and the eye can
|
|
631
704
|
// see where the WAN ends.
|
|
632
|
-
const kids = slaved.map((i, idx) => ` {light-blue-fg}${idx === slaved.length - 1 ? '└─' : '├─'} ${i}{/light-blue-fg}`);
|
|
705
|
+
const kids = slaved.map((i, idx) => ` {light-blue-fg}${idx === slaved.length - 1 ? '└─' : '├─'} ${mark(i)}{/light-blue-fg}`);
|
|
633
706
|
return [line, ...kids].join('\n');
|
|
634
707
|
}).join('\n')}`
|
|
635
708
|
: `{light-blue-fg}Nothing overhead -- no host stands over this district.{/light-blue-fg}`);
|
|
636
709
|
out.push(loose.length > 0
|
|
637
|
-
? `{light-blue-fg}ICONS IN REACH${slaved.length > 0 ? ' -- answering to nobody up there' : ''}:{/light-blue-fg}\n${loose.map(i => ` {light-blue-fg}${i}{/light-blue-fg}`).join('\n')}`
|
|
710
|
+
? `{light-blue-fg}ICONS IN REACH${slaved.length > 0 ? ' -- answering to nobody up there' : ''}:{/light-blue-fg}\n${loose.map(i => ` {light-blue-fg}${mark(i)}{/light-blue-fg}`).join('\n')}`
|
|
638
711
|
: slaved.length > 0
|
|
639
712
|
? `{light-blue-fg}Nothing else near -- everything around you hangs off the host.{/light-blue-fg}`
|
|
640
713
|
: `{light-blue-fg}Nothing near -- background noise around your signal.{/light-blue-fg}`);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { buildReproScene, reproArchetypeName } from '../factories/repro-scene.js';
|
|
2
|
+
import { saveForArchetype } from '../headless-harness.js';
|
|
3
|
+
import { REPRO_ACTOR, reproFixtureOf } from '../types/repro.js';
|
|
4
|
+
/**
|
|
5
|
+
* The scene and the runner a bench starts with.
|
|
6
|
+
*/
|
|
7
|
+
export function buildReproBoot(repro) {
|
|
8
|
+
const fixture = reproFixtureOf(repro);
|
|
9
|
+
const build = buildReproScene(fixture);
|
|
10
|
+
const archetype = reproArchetypeName(fixture);
|
|
11
|
+
return {
|
|
12
|
+
fixture,
|
|
13
|
+
build,
|
|
14
|
+
seed: build.seed,
|
|
15
|
+
archetype,
|
|
16
|
+
save: saveForArchetype(REPRO_ACTOR, archetype),
|
|
17
|
+
warnings: build.warnings,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* THE KNOBS THE SEED CANNOT CARRY, applied to a booted session.
|
|
22
|
+
*
|
|
23
|
+
* Call this after the session is up and before the first step (or, on
|
|
24
|
+
* the site, before the first status push) -- the point of both knobs is
|
|
25
|
+
* that the bench OPENS on a runner already in that state, so a reporter
|
|
26
|
+
* reads the condition on their first look rather than watching it be
|
|
27
|
+
* applied.
|
|
28
|
+
*
|
|
29
|
+
* Silent when the fixture asks for neither, and silent when the actor is
|
|
30
|
+
* missing: a bench whose runner did not boot has a larger problem than
|
|
31
|
+
* its damage boxes, and it will say so in the transcript.
|
|
32
|
+
*/
|
|
33
|
+
export async function applyReproFixtureState(game, fixture, actorName = REPRO_ACTOR) {
|
|
34
|
+
// START WOUNDED (fixture.wounded): the boxes go on before the first
|
|
35
|
+
// step, so the transcript opens on a runner who already needs the
|
|
36
|
+
// verb under test.
|
|
37
|
+
if (fixture.wounded) {
|
|
38
|
+
const runner = game.scene.getPlayerByName(actorName);
|
|
39
|
+
if (runner) {
|
|
40
|
+
if (fixture.wounded.physical)
|
|
41
|
+
runner.takeDamage(fixture.wounded.physical);
|
|
42
|
+
if (fixture.wounded.stun)
|
|
43
|
+
runner.takeStun(fixture.wounded.stun);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// NUYEN (fixture.nuyen): a credstick in the pack, because money lives
|
|
47
|
+
// on a carrier item (Player.currency sums them) and the pregen has none.
|
|
48
|
+
if (fixture.nuyen && fixture.nuyen > 0) {
|
|
49
|
+
const runner = game.scene.getPlayerByName(actorName);
|
|
50
|
+
if (runner) {
|
|
51
|
+
const { ItemFactory } = await import('../factories/item-factory.js');
|
|
52
|
+
const stick = await new ItemFactory().createItemFromJson({
|
|
53
|
+
name: 'Certified Credstick', description: 'Cash that does not ask questions.',
|
|
54
|
+
size: 'Tiny', category: 'Credstick', shape: 'Slab', color: 'Black', texture: 'Plastic',
|
|
55
|
+
rating: 'Common', weight: 0.1, transferable: true,
|
|
56
|
+
});
|
|
57
|
+
stick.addCurrency(fixture.nuyen);
|
|
58
|
+
runner.addInventory(stick);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=repro-boot.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import { fetchReviewQueue } from './backlog.js';
|
|
3
|
+
import { REPRO_ACTOR } from '../types/repro.js';
|
|
3
4
|
/**
|
|
4
5
|
* Resolve `<id>` or `<n>` against a FRESH review queue.
|
|
5
6
|
*
|
|
@@ -157,8 +158,14 @@ export function readReproFile(candidate) {
|
|
|
157
158
|
* since the last step and print them under the output they explain.
|
|
158
159
|
*/
|
|
159
160
|
/** The bench's actor. One name, used for the seat and for the
|
|
160
|
-
* visibility filter below.
|
|
161
|
-
|
|
161
|
+
* visibility filter below.
|
|
162
|
+
*
|
|
163
|
+
* Defined in types/repro.ts and re-exported here so the many callers
|
|
164
|
+
* that import it from the launcher keep working. It had to move: the
|
|
165
|
+
* site needs the same constant, and a module that reaches it through
|
|
166
|
+
* anything importing the scene builder would drag the game graph back
|
|
167
|
+
* across this file's lazy boundary. See the note on the constant. */
|
|
168
|
+
export { REPRO_ACTOR };
|
|
162
169
|
const stripTags = (s) => s.replace(/\{\/?[a-z-]+\}/g, '');
|
|
163
170
|
/**
|
|
164
171
|
* ONE STEP OF TRANSCRIPT, pure: the command's return string, then what
|
|
@@ -221,49 +228,23 @@ export function renderStepLines(step, text, fresh, actor = REPRO_ACTOR) {
|
|
|
221
228
|
return lines;
|
|
222
229
|
}
|
|
223
230
|
export async function dryRunRepro(repro) {
|
|
224
|
-
|
|
225
|
-
|
|
231
|
+
// The scene, the runner and the post-boot knobs all come from
|
|
232
|
+
// repro-boot, which is the ONE definition of what a bench boots as --
|
|
233
|
+
// shared with maka-cli.com's live bench so the transcript below and
|
|
234
|
+
// the session a reporter types into cannot describe different
|
|
235
|
+
// scenarios. See that module's header.
|
|
236
|
+
const [{ buildReproBoot, applyReproFixtureState }, { bootSession }] = await Promise.all([
|
|
237
|
+
import('./repro-boot.js'),
|
|
226
238
|
import('../headless-harness.js'),
|
|
227
239
|
]);
|
|
228
|
-
const
|
|
229
|
-
// The reader's framing doubles as the bench's story when the fixture
|
|
230
|
-
// has none of its own -- one sentence, written once.
|
|
231
|
-
if (!fixture.premise && repro.premise)
|
|
232
|
-
fixture.premise = repro.premise;
|
|
233
|
-
const built = buildReproScene(fixture);
|
|
240
|
+
const built = buildReproBoot(repro);
|
|
234
241
|
const events = [];
|
|
235
242
|
const session = await bootSession({
|
|
236
243
|
seed: built.seed,
|
|
237
|
-
players: [
|
|
244
|
+
players: [built.save],
|
|
238
245
|
events,
|
|
239
246
|
});
|
|
240
|
-
|
|
241
|
-
// step, so the transcript opens on a runner who already needs the
|
|
242
|
-
// verb under test.
|
|
243
|
-
if (fixture.wounded) {
|
|
244
|
-
const runner = session.game.scene.getPlayerByName(REPRO_ACTOR);
|
|
245
|
-
if (runner) {
|
|
246
|
-
if (fixture.wounded.physical)
|
|
247
|
-
runner.takeDamage(fixture.wounded.physical);
|
|
248
|
-
if (fixture.wounded.stun)
|
|
249
|
-
runner.takeStun(fixture.wounded.stun);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
// NUYEN (fixture.nuyen): a credstick in the pack, because money lives
|
|
253
|
-
// on a carrier item (Player.currency sums them) and the pregen has none.
|
|
254
|
-
if (fixture.nuyen && fixture.nuyen > 0) {
|
|
255
|
-
const runner = session.game.scene.getPlayerByName(REPRO_ACTOR);
|
|
256
|
-
if (runner) {
|
|
257
|
-
const { ItemFactory } = await import('../factories/item-factory.js');
|
|
258
|
-
const stick = await new ItemFactory().createItemFromJson({
|
|
259
|
-
name: 'Certified Credstick', description: 'Cash that does not ask questions.',
|
|
260
|
-
size: 'Tiny', category: 'Credstick', shape: 'Slab', color: 'Black', texture: 'Plastic',
|
|
261
|
-
rating: 'Common', weight: 0.1, transferable: true,
|
|
262
|
-
});
|
|
263
|
-
stick.addCurrency(fixture.nuyen);
|
|
264
|
-
runner.addInventory(stick);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
247
|
+
await applyReproFixtureState(session.game, built.fixture);
|
|
267
248
|
const lines = [];
|
|
268
249
|
// THE FIRST PAINT IS NOT PART OF STEP ONE. bootSession pushes every
|
|
269
250
|
// player's panels the moment the session comes up (B7NNNesy5gvRSbZpp),
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { rollPool, formatRoll } from './dice.js';
|
|
2
2
|
import { deviceDefensePool } from './matrix-intrusion.js';
|
|
3
3
|
import { isWatched } from './surveillance.js';
|
|
4
|
-
import { DEFAULT_DEVICE_RATING } from '../models/device.js';
|
|
5
4
|
function hiddenDevice(device) {
|
|
6
5
|
return {
|
|
7
6
|
name: device.name,
|
|
@@ -34,25 +33,27 @@ export function hiddenIconsIn(scene, room, viewer) {
|
|
|
34
33
|
.map(hiddenDevice);
|
|
35
34
|
// THE CAMERAS, which p.217 names FIRST. They are not a Device -- the
|
|
36
35
|
// cluster is derived from the room (surveillance.ts isWatched) -- so
|
|
37
|
-
//
|
|
38
|
-
// rating: an ordinary fixture, which canon prices at 2 (p.356).
|
|
36
|
+
// the rating rides the ROOM (Room.cameraRating), seeded per site.
|
|
39
37
|
//
|
|
40
38
|
// THE FIRST CUT GAVE THEM THEIR MASTER HOST'S RATING and that was
|
|
41
39
|
// wrong twice over. Canon first: p.233's slaving rule is about what a
|
|
42
|
-
// slave DEFENDS AN ATTACK with
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
40
|
+
// slave DEFENDS AN ATTACK with, and hiding is not an attack being
|
|
41
|
+
// defended. Then arithmetic: a rating-8 host gave its lenses 16 dice
|
|
42
|
+
// against a sweep the Data Processing limit caps near 5, so a camera
|
|
43
|
+
// on any corporate host was not hard to find, it was impossible --
|
|
44
|
+
// measured at zero finds in twelve looks.
|
|
45
|
+
//
|
|
46
|
+
// THE SECOND CUT hard-coded a 4 in here, which fixed the arithmetic
|
|
47
|
+
// and left the engine holding THREE answers to one question: this
|
|
48
|
+
// hid behind 4, commands/disable.ts defended a Data Spike with the
|
|
49
|
+
// HOST's pool, and the icon line showed no rating at all. A player
|
|
50
|
+
// could not have reconciled them because two of the three were never
|
|
51
|
+
// printed. One seeded number feeds all three now, so what the player
|
|
52
|
+
// reads is what it rolls (reporter's ruling: per-site, not global).
|
|
51
53
|
if (isWatched(room) && !room.camerasSpottedBy.has(viewer.name)) {
|
|
52
|
-
const rating = DEFAULT_DEVICE_RATING.lock ?? 2;
|
|
53
54
|
out.push({
|
|
54
55
|
name: 'Camera cluster',
|
|
55
|
-
hide: { pool: Math.max(0,
|
|
56
|
+
hide: { pool: Math.max(0, room.cameraRating * 2), label: 'Device Rating + Firewall' },
|
|
56
57
|
reveal: (v) => { room.camerasSpottedBy.add(v); },
|
|
57
58
|
});
|
|
58
59
|
}
|
|
@@ -98,28 +99,44 @@ export function spotHiddenIcons(scene, actor, room) {
|
|
|
98
99
|
+ actor.matrixActionPenalty + actor.woundModifier - actor.sustainingPenalty);
|
|
99
100
|
const roll = rollPool(pool, actor.matrixAttribute('dataProcessing'), { gremlins: actor.deckGremlins });
|
|
100
101
|
const found = [];
|
|
101
|
-
|
|
102
|
+
let missed = 0;
|
|
102
103
|
const against = [];
|
|
103
104
|
for (const icon of hidden) {
|
|
104
105
|
const resist = rollPool(icon.hide.pool);
|
|
105
|
-
against.push({ name: icon.name, label: icon.hide.label, roll: resist });
|
|
106
106
|
// Ties go to the hider: "on a tie or more hits by the defender, it
|
|
107
107
|
// stays hidden and out of reach" (p.236).
|
|
108
108
|
if (roll.hits > resist.hits) {
|
|
109
109
|
icon.reveal(actor.name);
|
|
110
110
|
found.push(icon.name);
|
|
111
|
+
against.push({ name: icon.name, label: icon.hide.label, roll: resist });
|
|
111
112
|
}
|
|
112
113
|
else {
|
|
113
|
-
missed
|
|
114
|
+
missed += 1;
|
|
115
|
+
// THE NAME DOES NOT LEAVE THIS BRANCH. Only the dice do -- see
|
|
116
|
+
// ISpotOutcome.missed. Attaching the name here "just for the
|
|
117
|
+
// mechanics line" is exactly how it leaked the first time.
|
|
118
|
+
against.push({ roll: resist });
|
|
114
119
|
}
|
|
115
120
|
}
|
|
116
121
|
return { found, missed, roll, against };
|
|
117
122
|
}
|
|
118
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* The mechanics lines for a sweep.
|
|
125
|
+
*
|
|
126
|
+
* AN ICON YOU DID NOT BEAT IS NOT NAMED HERE (user ruling 2026-09-16).
|
|
127
|
+
* Your own roll is always shown -- it is yours. What it was rolled
|
|
128
|
+
* against is shown only where you won it: a loss prints its dice and
|
|
129
|
+
* calls the roller "something", because naming it would hand over for
|
|
130
|
+
* free precisely what the opposed test exists to charge for (p.235:
|
|
131
|
+
* the type of an icon and its device rating are things a Matrix
|
|
132
|
+
* Perception HIT buys).
|
|
133
|
+
*/
|
|
119
134
|
export function spotMechanics(outcome) {
|
|
120
135
|
return [
|
|
121
136
|
`Matrix Perception, silent icons (Computer + Intuition [Data Processing]): ${formatRoll(outcome.roll)}`,
|
|
122
|
-
...outcome.against.map(a =>
|
|
137
|
+
...outcome.against.map(a => (a.name
|
|
138
|
+
? ` ${a.name} hides (${a.label}): ${formatRoll(a.roll)}`
|
|
139
|
+
: ` something holds its cover: ${formatRoll(a.roll)}`)),
|
|
123
140
|
];
|
|
124
141
|
}
|
|
125
142
|
/**
|
|
@@ -135,10 +152,14 @@ export function spotLine(outcome) {
|
|
|
135
152
|
if (outcome.found.length > 0) {
|
|
136
153
|
lines.push(`You pick ${outcome.found.length === 1 ? 'an icon' : `${outcome.found.length} icons`} out of the noise -- ${outcome.found.join(', ')}, running silent and not quiet enough.`);
|
|
137
154
|
}
|
|
138
|
-
if (outcome.missed
|
|
139
|
-
|
|
155
|
+
if (outcome.missed > 0) {
|
|
156
|
+
// A COUNT AND NOTHING ELSE. You learn that the room is holding
|
|
157
|
+
// something back -- which is what a hit buys under p.235-236 -- and
|
|
158
|
+
// not what it is. `missed` is a number precisely so this line cannot
|
|
159
|
+
// grow a name later.
|
|
160
|
+
lines.push(outcome.missed === 1
|
|
140
161
|
? `Something else here is running silent and you cannot pin it -- a shape in the static that stops when you look at it. (Look again; each sweep is its own Matrix Perception, p.241.)`
|
|
141
|
-
: `${outcome.missed
|
|
162
|
+
: `${outcome.missed} more icons are running silent here, and none of them will hold still. (Look again; each sweep is its own Matrix Perception, p.241.)`);
|
|
142
163
|
}
|
|
143
164
|
return lines.join('\n');
|
|
144
165
|
}
|
|
@@ -6,6 +6,10 @@ import { Actions } from '../../tools/actions/actions.class.js';
|
|
|
6
6
|
import { Log } from '../../tools/log/log.class.js';
|
|
7
7
|
import { AI } from '../../tools/ai/ai.class.js';
|
|
8
8
|
import { spawnSelf } from '../../tools/self-spawn/self-spawn.js';
|
|
9
|
+
// A value import, not a type-only one, for reproFixtureOf -- safe
|
|
10
|
+
// against this file's "no game graph on `maka --help`" rule because
|
|
11
|
+
// types/repro.ts reaches only two leaf enum modules.
|
|
12
|
+
import { reproFixtureOf } from './sideQuest/types/repro.js';
|
|
9
13
|
/**
|
|
10
14
|
* SPAWN THE GAME AND BE ITS JANITOR.
|
|
11
15
|
*
|
|
@@ -77,9 +81,12 @@ export async function spawnGame(launchFilePath, logFilePath) {
|
|
|
77
81
|
*/
|
|
78
82
|
async function launchReproBench(repro, item, makaDir) {
|
|
79
83
|
const { buildReproScene, reproArchetypeName } = await import('./sideQuest/factories/repro-scene.js');
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
// The one premise rule, shared with the dry run and the web bench --
|
|
85
|
+
// see reproFixtureOf. This path stops at the scene: the game rolls the
|
|
86
|
+
// character itself from `archetype` below, so it deliberately does NOT
|
|
87
|
+
// go through repro-boot, which would pull the headless harness (and
|
|
88
|
+
// with it game.ts) into the PARENT process that only spawns the child.
|
|
89
|
+
const fixture = reproFixtureOf(repro);
|
|
83
90
|
let built;
|
|
84
91
|
try {
|
|
85
92
|
built = buildReproScene(fixture);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.228.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|