@maka/maka-cli 5.238.0 → 5.239.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.238.0",
3
+ "version": "5.239.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.",
@@ -1,6 +1,6 @@
1
1
  import { Command } from './command.js';
2
2
  import { movePersonaWith } from '../utilities/matrix-roster.js';
3
- import { resolveHostInReach, hostsInReach, gridVicinity, hostOver } from '../utilities/grid-reach.js';
3
+ import { resolveHostInReach, hostsInReach, gridVicinity, hostOver, hostDoorOpenFor } from '../utilities/grid-reach.js';
4
4
  import { hint } from '../utilities/hints.js';
5
5
  import { hostLabel } from '../utilities/grid-names.js';
6
6
  import { billAction } from '../utilities/action-cost.js';
@@ -61,11 +61,21 @@ export class EnterHostCommand extends Command {
61
61
  // actions to get a mark has bought the door, and charging them a
62
62
  // test here would be inventing a rule the book explicitly denies
63
63
  // ("There is no test involved").
64
- const marks = target.hostMarksBy.get(actor.name) ?? 0;
65
- if (marks < 1 && !target.hostCracked && !target.hostSanctioned) {
64
+ //
65
+ // AND IT IS THE ONLY GATE (DxfeZTBrP6Bu7MLF4). It used to read `||
66
+ // hostCracked`, which is the SITE's state and never expires -- see
67
+ // grid-reach.ts hostDoorOpenFor for why that was a door nobody could
68
+ // ever close again. A persona whose keys are gone gets its own line:
69
+ // the wreckage it can see from out here is real, and saying "doesn't
70
+ // know you" about a node it personally tore open would read as the
71
+ // engine forgetting the last ten minutes.
72
+ if (!hostDoorOpenFor(target, actor)) {
73
+ const wrecked = target.hostCracked;
66
74
  return [
67
- `{light-blue-fg}${hostLabel(target, { capital: true })} doesn't know you. Its architecture has no door shaped like your icon.{/light-blue-fg}`,
68
- hint(`A host opens to anyone holding a MARK on it (p.239) -- "mark ${target.name}" softens it, or "hack" takes one by force.`),
75
+ wrecked
76
+ ? `{light-blue-fg}${hostLabel(target, { capital: true })} is still hanging open where you broke it -- and it does not know YOU. Your keys are out of the lattice; the architecture has nothing shaped like your icon to open for.{/light-blue-fg}`
77
+ : `{light-blue-fg}${hostLabel(target, { capital: true })} doesn't know you. Its architecture has no door shaped like your icon.{/light-blue-fg}`,
78
+ hint(`A host opens to anyone holding a MARK on it, and marks die with the session you placed them in (p.239, p.236) -- "hack ${target.name}" keys it quietly, "force ${target.name}" takes one loud.`),
69
79
  ].join('\n');
70
80
  }
71
81
  // A Complex Action (p.239) -- billed only inside a Combat Turn.
@@ -1,5 +1,5 @@
1
1
  import { whereIsThat } from '../utilities/matrix-roster.js';
2
- import { resolveHostInReach, roomsInReach, noiseNote, gridVicinity } from '../utilities/grid-reach.js';
2
+ import { resolveHostInReach, roomsInReach, noiseNote, gridVicinity, hostDoorOpenFor } from '../utilities/grid-reach.js';
3
3
  import { perceptionTest } from '../utilities/perception.js';
4
4
  import { matchesHostName, matchesCameraName, hostLabel } from '../utilities/grid-names.js';
5
5
  import { Door } from '../models/door.js';
@@ -392,7 +392,7 @@ export class LookCommand extends Command {
392
392
  return `That file is on the ${room.offlineServer} -- a box with no Matrix presence. Nothing out here reaches it; your body has to be in ${room.name} (p.233).`;
393
393
  }
394
394
  if (reach === 'enter') {
395
- return `That file is INSIDE the host -- from out on the grid you have its shell and nothing through it (p.246). ${(room.hostMarksBy.get(this.actor.name) ?? 0) > 0 || room.hostCracked ? `"enter" to go in.` : `A mark opens the door: "hack ${hostLabel(room)}" (quiet) or "force ${hostLabel(room)}" (loud), then "enter".`}`;
395
+ return `That file is INSIDE the host -- from out on the grid you have its shell and nothing through it (p.246). ${hostDoorOpenFor(room, this.actor) ? `"enter" to go in.` : `A mark opens the door: "hack ${hostLabel(room)}" (quiet) or "force ${hostLabel(room)}" (loud), then "enter".`}`;
396
396
  }
397
397
  // The host's seal covers the HOST's archive. A file on an offline
398
398
  // server is not in there -- cracking the building's host does
@@ -1,6 +1,6 @@
1
1
  import { Command } from './command.js';
2
2
  import { whereIsThat } from '../utilities/matrix-roster.js';
3
- import { gridVicinity } from '../utilities/grid-reach.js';
3
+ import { gridVicinity, hostDoorOpenFor } from '../utilities/grid-reach.js';
4
4
  import { fileReach } from '../utilities/planes.js';
5
5
  import { canReach, ensureAtSpot, sealedRouteTo } from '../utilities/spots.js';
6
6
  import { hint } from '../utilities/hints.js';
@@ -143,7 +143,7 @@ export class TakeCommand extends Command {
143
143
  return `Those files are on the ${room.offlineServer} -- off the network entirely. No persona reaches that box; your BODY has to be in ${room.name} (p.233).`;
144
144
  }
145
145
  if (reach === 'enter') {
146
- return `Those files are INSIDE the host, and you're out on the grid looking at its shell (p.246). ${room.hostMarksBy.has(this.actor.name) || room.hostCracked ? `"enter" first.` : `Get a mark on it and "enter" -- or "hack" your way in.`}`;
146
+ return `Those files are INSIDE the host, and you're out on the grid looking at its shell (p.246). ${hostDoorOpenFor(room, this.actor) ? `"enter" first.` : `Get a mark on it and "enter" -- or "hack" your way in.`}`;
147
147
  }
148
148
  // Same split as look.ts: the host's seal does not reach a box that
149
149
  // is not on the network. The air gap IS the server's security.
@@ -291,7 +291,7 @@ export class TakeCommand extends Command {
291
291
  if (far.reach === 'offline')
292
292
  return `${far.item.name} is on the ${far.room.offlineServer} in ${far.room.name} -- an offline server, off the network entirely. No persona reaches that box; your BODY has to be in ${far.room.name} (p.233).`;
293
293
  if (far.reach === 'host')
294
- return `${far.item.name} is INSIDE ${far.room.name}'s host, and you're out on the grid looking at its shell (p.246). ${far.room.hostMarksBy.has(this.actor.name) || far.room.hostCracked ? `"enter ${far.room.name}" first.` : `Get a mark on it and "enter" -- or "hack" your way in.`}`;
294
+ return `${far.item.name} is INSIDE ${far.room.name}'s host, and you're out on the grid looking at its shell (p.246). ${hostDoorOpenFor(far.room, this.actor) ? `"enter ${far.room.name}" first.` : `Get a mark on it and "enter" -- or "hack" your way in.`}`;
295
295
  if (far.reach === 'open')
296
296
  return `${far.item.name} is loose on the grid -- a dim icon. ${hint(`"download ${far.item.name}" writes it to a chip from here.`)}`;
297
297
  }
@@ -337,7 +337,7 @@ export class TakeCommand extends Command {
337
337
  return `The ${item.name} sits on the ${room.offlineServer} -- a box with no Matrix presence at all. No persona reaches it from anywhere: get your BODY into ${room.name} and cable in (p.233).`;
338
338
  }
339
339
  if (reach === 'enter') {
340
- return `The ${item.name} is INSIDE the host -- you're on the outside grid, reading its shell (p.246). ${(room.hostMarksBy.get(this.actor.name) ?? 0) > 0 || room.hostCracked ? `"enter" first.` : `Get a mark on it and "enter" -- or "hack" your way in.`}`;
340
+ return `The ${item.name} is INSIDE the host -- you're on the outside grid, reading its shell (p.246). ${hostDoorOpenFor(room, this.actor) ? `"enter" first.` : `Get a mark on it and "enter" -- or "hack" your way in.`}`;
341
341
  }
342
342
  // A LOOSE END IS ERASED, NOT CARRIED (win-condition-seed.ts 'erase',
343
343
  // 2026-09-14): pulling the footage down to a chip leaves the desk's
@@ -1614,5 +1614,32 @@
1614
1614
  // names the cable. The AR overlay deliberately says NOTHING: that
1615
1615
  // surface annotates broadcasts, which is its stated contract, and the
1616
1616
  // first cut of this violated it.
1617
- export const ENGINE_VERSION = '1.87.0';
1617
+ // 1.88.0 (2026-09-17): THE DOOR INTO A HOST IS A MARK AND NOTHING ELSE
1618
+ // (DxfeZTBrP6Bu7MLF4, Maka: "I shouldn't be able to re-enter the host
1619
+ // if I have no marks on it"). `enter` accepted Room.hostCracked as a
1620
+ // substitute for the key. That flag is ROOM-GLOBAL and permanent for
1621
+ // the save -- once anybody took a node to three marks its door never
1622
+ // shut again, for anybody. The reporter's own transcript walks it:
1623
+ // three marks forced into the Approach host, cracked, JACK OUT (canon
1624
+ // deletes every mark they placed, p.242), jack back in reading MARKS
1625
+ // 0 held -- and straight back inside with the Patrol locking on.
1626
+ // Canon is one sentence (p.239, RAG-checked 2026-09-17): "a host
1627
+ // allows anyone to enter if they've got a mark", marks "only last a
1628
+ // single Matrix session" (p.236), three of them buy nothing that
1629
+ // outlives them, and a host's owner cannot be changed (p.237). This
1630
+ // is 1.56.0's ruling (spcebZTKN24GYRHXW, a cracked host is not a host
1631
+ // you hold marks on) finally applied to the one verb the flag was
1632
+ // actually a door for. The crack's WORLD consequences are untouched:
1633
+ // the vault still hangs open, the archive still reads unsealed, the
1634
+ // maglocks it released stay released -- those are facts about the
1635
+ // site, and the key is a fact about you.
1636
+ // ONE GATE, ONE ANSWER: grid-reach.ts hostDoorOpenFor is now the only
1637
+ // place that decides, and every surface that says "enter" asks it --
1638
+ // the verb, look/take's file refusals, and the grid icon, which used
1639
+ // to print `("enter" walks in)` under a crack and would otherwise have
1640
+ // become an invitation the verb refuses.
1641
+ // THE HAVEN IS THE ONE EXEMPTION and is labelled as an engine ruling,
1642
+ // not canon: a sanctioned host (Room.hostSanctioned, JackPoint) is a
1643
+ // members' place, and membership is the mark.
1644
+ export const ENGINE_VERSION = '1.88.0';
1618
1645
  //# sourceMappingURL=engine-version.js.map
@@ -41,6 +41,42 @@ export function hostsInReach(rooms, actor) {
41
41
  export function hostOver(room) {
42
42
  return room?.hasNode ? room : undefined;
43
43
  }
44
+ /**
45
+ * IS THE DOOR OPEN TO THIS PERSONA -- the one answer, so that the verb
46
+ * that crosses and every hint that says "enter" cannot disagree
47
+ * (DxfeZTBrP6Bu7MLF4, Maka: "I shouldn't be able to re-enter the host if
48
+ * I have no marks on it").
49
+ *
50
+ * p.239 is one sentence long: "Marks required 1 ... a host allows anyone
51
+ * to enter if they've got a mark." RAG-checked 2026-09-17, which also
52
+ * settles the other half: marks "only last a single Matrix session and
53
+ * are deleted when you reboot" (p.236), reaching three of them buys
54
+ * nothing that outlives them, and a host's owner cannot be changed
55
+ * (p.237) -- so there is no state in canon that stands in for the key.
56
+ *
57
+ * WHAT USED TO STAND IN FOR IT. `enter` accepted Room.hostCracked, which
58
+ * is ROOM-GLOBAL and permanent for the save: once anybody took the node
59
+ * to three marks, that door never closed again -- not for the decker who
60
+ * jacked out (canon wipes their marks, p.242) and not for a team-mate
61
+ * who had never touched it. That is the same confusion 1.56.0 named for
62
+ * `hack`/`mark` (spcebZTKN24GYRHXW: a cracked host is not a host you
63
+ * hold marks on) with the one verb the flag was actually a door for left
64
+ * out. The crack's WORLD consequences are untouched -- a vault hanging
65
+ * open stays open, its archive stays listed -- because those are facts
66
+ * about the site and this is a fact about your keys.
67
+ *
68
+ * THE HAVEN IS THE ONE EXEMPTION, and it is an engine ruling, not canon:
69
+ * a sanctioned host (utilities/jackpoint.ts, Room.hostSanctioned) is a
70
+ * members' place you belong to, and membership is the mark. Labelled
71
+ * here rather than hidden in a boolean.
72
+ */
73
+ export function hostDoorOpenFor(room, actor) {
74
+ if (!room.hasNode)
75
+ return false;
76
+ if (room.hostSanctioned)
77
+ return true;
78
+ return (room.hostMarksBy.get(actor.name) ?? 0) >= 1;
79
+ }
44
80
  /**
45
81
  * A host by any name the game shows for it (grid-names.ts), from
46
82
  * anywhere on the grid. Bare "host" (or nothing) means the one over the
@@ -1,6 +1,6 @@
1
1
  import { sameHostSide } from '../models/player.js';
2
2
  import { personasInside } from './matrix-roster.js';
3
- import { hostsInReach, hostLine, roomsInReach, gridVicinity } from './grid-reach.js';
3
+ import { hostsInReach, hostLine, roomsInReach, gridVicinity, hostDoorOpenFor } from './grid-reach.js';
4
4
  import { NPC } from '../models/npc.js';
5
5
  import { hostLabel, hostPurposeFor } from './grid-names.js';
6
6
  import { isLinked } from './comm-style.js';
@@ -142,7 +142,17 @@ export function gridIcons(scene, actor, room) {
142
142
  icons.push(`● ${hostLabel(room, { capital: true })} -- three spheres turning over rooms that were never sold to anyone. You're on the list; nothing here is hunting.`);
143
143
  }
144
144
  else if (room.hostCracked) {
145
- icons.push(`● ${hostLabel(room, { capital: true })}${hr}${purpose} -- cracked open, security in shreds. Whatever it guarded is unsealed, inside. ("enter" walks in)`);
145
+ // THE WRECKAGE IS THE SITE'S; THE DOOR IS STILL YOURS TO HOLD
146
+ // (DxfeZTBrP6Bu7MLF4). This line offered `"enter" walks in` on the
147
+ // room-global crack alone, so a persona whose marks a jack-out had
148
+ // just wiped (p.242) read an invitation the verb then refused --
149
+ // or, before the verb was fixed, accepted, which is the report.
150
+ // Both halves stay true now: the vault hangs open, and the key is
151
+ // per-persona and dies with the session (p.236).
152
+ const way = hostDoorOpenFor(room, actor)
153
+ ? `("enter" walks in on your mark)`
154
+ : `("hack ${hostLabel(room)}" or "force ${hostLabel(room)}" for a mark -- yours are gone, and a host still asks for one, p.239)`;
155
+ icons.push(`● ${hostLabel(room, { capital: true })}${hr}${purpose} -- cracked open, security in shreds. Whatever it guarded is unsealed, inside. ${way}`);
146
156
  }
147
157
  else {
148
158
  const myMarks = room.hostMarksBy.get(actor.name) ?? 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.238.0",
3
+ "version": "5.239.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.",