@maka/maka-cli 5.148.0 → 5.150.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.148.0",
3
+ "version": "5.150.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.",
@@ -38,10 +38,11 @@ export class BacklogCommand extends Command {
38
38
  static verb = "backlog";
39
39
  // Writes to the player OWN board over their auth token -- never an NPC.
40
40
  static humanOnly = true;
41
- static description = 'File a play-tester item that somebody acts on -- it goes to maka-cli.com, keeps a link to this session, and comes back checked against the rulebooks. "backlog <what you noticed>" files one; "backlog list [status]" reads the board. For private scribbles, use "note".';
41
+ static description = 'File a play-tester item that somebody acts on -- it goes to maka-cli.com, keeps a link to this session, and comes back checked against the rulebooks. "backlog <what you noticed>" files one; "backlog list [status]" reads the board; "backlog web" opens the live board in your browser. For private scribbles, use "note".';
42
42
  static subcommands = [
43
43
  { usage: 'backlog <text>', short: 'File an item against this session.' },
44
44
  { usage: 'backlog list', short: 'The whole board, what needs you last.' },
45
+ { usage: 'backlog web', short: 'Open the live backlog board in your browser.' },
45
46
  { usage: 'backlog <n>', short: 'Read board item n in full.' },
46
47
  { usage: 'backlog repro <n>', short: 'Play the repro bench for board item n.' },
47
48
  { usage: 'backlog close <n> [why]', short: 'Close item n outright -- filed in error, duplicate, no longer real.' },
@@ -71,6 +72,12 @@ export class BacklogCommand extends Command {
71
72
  if (args.length === 0 || sub === 'list') {
72
73
  return this.list(args[1]);
73
74
  }
75
+ // Claimed here, same reason "accept"/"reject"/"repro"/"close" are:
76
+ // every unclaimed word past this point is report text, and "backlog
77
+ // web" must open the board, not file an item whose text is "web".
78
+ if (sub === 'web') {
79
+ return this.webBoard();
80
+ }
74
81
  // A BARE NUMBER READS THAT ROW OF THE BOARD, and it has to be caught
75
82
  // here for the same reason `list` and `review` are: everything that
76
83
  // reaches the bottom of this method FILES AN ITEM. Without this,
@@ -118,6 +125,23 @@ export class BacklogCommand extends Command {
118
125
  }
119
126
  return this.file(args.join(' '));
120
127
  }
128
+ /**
129
+ * "backlog web" (user request, 2026-09-10): the same /cli-login
130
+ * handoff as "sheet web" (sheet.ts), landing on the shared board --
131
+ * /backlog, already on the NEXT_ALLOWLIST. The token is guaranteed
132
+ * present here: execute() already refused above if authToken() was
133
+ * empty, before any subcommand (this one included) could be reached.
134
+ */
135
+ async webBoard() {
136
+ const { buildBacklogBoardUrl } = await import('../utilities/web-character-generator.js');
137
+ const url = buildBacklogBoardUrl(authToken());
138
+ const { openBrowser } = await import('../../../../tools/browser/open-browser.js');
139
+ openBrowser(url);
140
+ return [
141
+ `Your backlog board is opening in the browser -- the game keeps running right here.`,
142
+ ` ${url}`,
143
+ ].join('\n');
144
+ }
121
145
  async file(text) {
122
146
  const game = this.game;
123
147
  // LAZY, and not for speed: shared-run.ts pulls in tools/version/
@@ -51,6 +51,21 @@ export class LookCommand extends Command {
51
51
  return `${containerHit.description}\n Open. ${items.length === 0 ? 'Empty.'
52
52
  : `Inside: ${items.map(i => i.quantity > 1 ? `${i.name} ×${i.quantity}` : i.name).join(', ')}.`}`;
53
53
  }
54
+ // "look deck" (W7fzZRb6Asa7Qqjzg): a decker's cyberdeck is
55
+ // usually branded gear ("Sony CIY-720"), not literally named
56
+ // "deck" -- so the generic name-match below missed it entirely
57
+ // and "look deck" read "You cannot see deck" even carrying one.
58
+ // Resolved through the SAME lookup "deck" (deck.ts) already uses
59
+ // for the active/best-carried deck, then handed to the ordinary
60
+ // inspectItem by its real name so the two verbs describe one
61
+ // object identically.
62
+ if (target === 'deck') {
63
+ const carried = this.actor.getAnyCyberdeck();
64
+ if (carried) {
65
+ this.logger.write(`Looking at active deck '${carried.name}' in ${this.actor.name} inventory.`);
66
+ return this.inspectItem(carried.name, this.actor.inventory);
67
+ }
68
+ }
54
69
  if (this.actor.inventory.hasItem(target)) {
55
70
  this.logger.write(`Looking at item '${target}' in ${this.actor.name} inventory.`);
56
71
  return this.inspectItem(target, this.actor.inventory);
@@ -185,6 +185,7 @@ import { clockTime, worldNow } from './utilities/world-clock.js';
185
185
  import { CommandFactory } from './factories/command-factory.js';
186
186
  import { SceneSynthesizer } from './factories/scene-factory.js';
187
187
  import { SceneSeedGenerator, FIXER_NAME, isFixerName } from './factories/scene-seed-generator.js';
188
+ import { draftFailureNote } from './utilities/draft-failure-note.js';
188
189
  import { CALL_ICON, CALL_COLOR, END_CALL_SENTINEL } from './utilities/comm-style.js';
189
190
  import { AI } from '../../../tools/ai/ai.class.js';
190
191
  import { BULLET } from './utilities/log-style.js';
@@ -4147,33 +4148,7 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
4147
4148
  // caller but the ask-for-data-work door passes.
4148
4149
  const nextSeed = await SceneSeedGenerator.generate(this._logFilePath, nextTier, this.player, this.activeRumor, localCrew, undefined, runType);
4149
4150
  if (!nextSeed) {
4150
- // NAME the cause when it's the PROVIDER, not luck (real session:
4151
- // six Krow calls into an out-of-credits key, indistinguishable
4152
- // from flaky clients). Credit/auth failures are the player's to
4153
- // fix -- say so, out of character, once per ping.
4154
- const why = SceneSeedGenerator.lastFailure ?? '';
4155
- // TELL THE TRUTH ABOUT WHOSE FAULT IT IS (playtest 2026-08-26).
4156
- // The old test carried `invalid.*key`, meant for "invalid API
4157
- // key" -- but `.*` spans the whole message, so a skeleton
4158
- // validation error reading "Skeleton INVALID: ... locked doors
4159
- // and KEY placements" matched, and a map-generation bug was
4160
- // reported to the player as a billing problem. Sending someone to
4161
- // rotate a working key is worse than saying nothing.
4162
- //
4163
- // Every pattern here is now anchored to the phrase it actually
4164
- // means: a bare "invalid" or a stray "key" no longer counts, and
4165
- // the status codes need word boundaries so they can't match
4166
- // inside some other number.
4167
- const providerDown = /credit balance|billing|quota|insufficient (?:credit|fund|quota|balance)|invalid[\s_-]*api[\s_-]*key|api[\s_-]*key (?:is )?(?:invalid|expired|missing|not)|unauthorized|authentication|\b(?:401|403|429)\b|Plans & Billing/i.test(why);
4168
- // A structural failure is OURS, not the player's -- name it as
4169
- // the generator's problem so nobody goes hunting their billing
4170
- // page, and keep it short: the fiction already covers the beat.
4171
- const structural = !providerDown && /^Skeleton invalid|validation failed|isn't placed|unreachable from the start/i.test(why);
4172
- const opsNote = providerDown
4173
- ? `\n{yellow-fg}(Out of character: the AI provider refused -- "${why.slice(0, 120)}". Jobs can't generate until the API key/credits are fixed.){/yellow-fg}`
4174
- : structural
4175
- ? `\n{yellow-fg}(Out of character: the job generator couldn't lay out a solvable map this time -- nothing wrong with your key or credits. Call again; the next draft usually takes.){/yellow-fg}`
4176
- : '';
4151
+ const opsNote = draftFailureNote(SceneSeedGenerator.lastFailure ?? '');
4177
4152
  return `Incoming call -- ${FIXER_NAME}: "Job fell through, chummer. Client got cold feet before the ink dried. Give me another call in a bit -- I'll shake something loose."${opsNote}`;
4178
4153
  }
4179
4154
  // HUB sessions: the draft lands as a MESSAGE, never a teleport (real
@@ -0,0 +1,66 @@
1
+ /**
2
+ * THE OUT-OF-CHARACTER NOTE ON A FAILED JOB DRAFT (game.ts's
3
+ * draftNextScene). SceneSeedGenerator.generate() gives up after three
4
+ * pipeline attempts and hands back a bare error string; Krow's "Job
5
+ * fell through" callback fires either way, and this decides what --
6
+ * if anything -- rides after it, so the player can tell "you did
7
+ * something wrong" from "this is on us" from "pure bad luck, try
8
+ * again."
9
+ *
10
+ * Extracted to its own module (2026-09-10) so the classification --
11
+ * pure string matching against known provider/generator error prose --
12
+ * can be pinned directly, without standing up a Game instance or
13
+ * mocking SceneSeedGenerator to reach it.
14
+ *
15
+ * TELL THE TRUTH ABOUT WHOSE FAULT IT IS (playtest 2026-08-26). The old
16
+ * test carried `invalid.*key`, meant for "invalid API key" -- but `.*`
17
+ * spans the whole message, so a skeleton validation error reading
18
+ * "Skeleton INVALID: ... locked doors and KEY placements" matched, and
19
+ * a map-generation bug was reported to the player as a billing problem.
20
+ * Sending someone to rotate a working key is worse than saying nothing.
21
+ * Every pattern here is anchored to the phrase it actually means: a
22
+ * bare "invalid" or a stray "key" no longer counts, and the status
23
+ * codes need word boundaries so they can't match inside some other
24
+ * number.
25
+ */
26
+ export function draftFailureNote(why) {
27
+ // NAME THE CAUSE WHEN IT'S THE PROVIDER, NOT LUCK (real session: six
28
+ // Krow calls into an out-of-credits key, indistinguishable from flaky
29
+ // clients). Credit/auth failures are the player's to fix -- say so,
30
+ // out of character, once per ping.
31
+ // Widened 2026-09-10: the site's own current wording (ai-error-envelope.ts
32
+ // classifyUpstream's upstream_billing branch) reads "has run out of
33
+ // credit at its provider, or its key was rejected" -- neither phrase
34
+ // matched any pattern below, so the ORIGINAL motivating bug (the "six
35
+ // Krow calls into an out-of-credits key" session) would silently fall
36
+ // through to no note again today, on the site's real current message.
37
+ const providerDown = /credit balance|billing|quota|insufficient (?:credit|fund|quota|balance)|run out of credit|key was rejected|invalid[\s_-]*api[\s_-]*key|api[\s_-]*key (?:is )?(?:invalid|expired|missing|not)|unauthorized|authentication|\b(?:401|403|429)\b|Plans & Billing/i.test(why);
38
+ // TRANSIENT INFRASTRUCTURE, not billing and not a generation bug
39
+ // (real session, e8dLKM9j5P9yQn5yz's sibling report: a fresh
40
+ // character's very first homecoming draft, plus four manual "call
41
+ // Krow" retries, all failed in under half a second each against "The
42
+ // AI provider behind maka-cli.com is overloaded or down" --
43
+ // ai-error-envelope.ts's upstream_unavailable/rate_limited/
44
+ // upstream_timeout/upstream_error text, none of which this classifier
45
+ // recognized, so every one of those five calls read as the same
46
+ // unexplained "Job fell through" with nothing after it --
47
+ // indistinguishable from a broken loop rather than one outage. These
48
+ // are OURS and retryable, same tone as `structural` below, but a
49
+ // different truth: nothing was malformed, the provider just wasn't
50
+ // answering. Matched on the SITE'S OWN wording (ai-error-envelope.ts
51
+ // classifyUpstream/classifyTransportFailure) rather than status
52
+ // codes, since only the prose crosses the wire.
53
+ const providerOutage = !providerDown && /overloaded or down|rate-limited by its provider|took too long to answer|could not (?:complete that request|reach its ai provider)/i.test(why);
54
+ // A structural failure is OURS, not the player's -- name it as the
55
+ // generator's problem so nobody goes hunting their billing page, and
56
+ // keep it short: the fiction already covers the beat.
57
+ const structural = !providerDown && !providerOutage && /^Skeleton invalid|validation failed|isn't placed|unreachable from the start/i.test(why);
58
+ return providerDown
59
+ ? `\n{yellow-fg}(Out of character: the AI provider refused -- "${why.slice(0, 120)}". Jobs can't generate until the API key/credits are fixed.){/yellow-fg}`
60
+ : providerOutage
61
+ ? `\n{yellow-fg}(Out of character: the AI provider maka-cli.com talks to is overloaded, rate-limited, or unreachable right now -- nothing wrong with your account or your key. Give it a few minutes and call again.){/yellow-fg}`
62
+ : structural
63
+ ? `\n{yellow-fg}(Out of character: the job generator couldn't lay out a solvable map this time -- nothing wrong with your key or credits. Call again; the next draft usually takes.){/yellow-fg}`
64
+ : '';
65
+ }
66
+ //# sourceMappingURL=draft-failure-note.js.map
@@ -82,6 +82,18 @@ export function buildCharacterSheetUrl(resumeToken, slug) {
82
82
  const next = `${CHARACTERS_PAGE}/${encodeURIComponent(slug)}`;
83
83
  return `${cliLoginUrl()}?t=${Date.now()}#token=${encodeURIComponent(resumeToken)}&next=${next}`;
84
84
  }
85
+ const BACKLOG_PAGE = '/backlog';
86
+ /**
87
+ * "backlog web" (user request, 2026-09-10): the same /cli-login handoff
88
+ * as the live sheet, landing on the shared backlog board -- /backlog,
89
+ * already on cli-login-next.ts's NEXT_ALLOWLIST as a bare path. Unlike
90
+ * buildCharacterSheetUrl/buildDossierUrl there is no per-item deep link
91
+ * to validate here: the board itself is the destination, not a row on
92
+ * it.
93
+ */
94
+ export function buildBacklogBoardUrl(resumeToken) {
95
+ return `${cliLoginUrl()}?t=${Date.now()}#token=${encodeURIComponent(resumeToken)}&next=${BACKLOG_PAGE}`;
96
+ }
85
97
  /**
86
98
  * WHAT THE CLI SAYS ON ITS WAY OUT of the chargen handoff. Extracted
87
99
  * from the command handler so the two things that matter about it are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.148.0",
3
+ "version": "5.150.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.",