@eventmodelers/cli 1.0.57 → 1.0.58

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 (3) hide show
  1. package/README.md +13 -4
  2. package/cli.js +55 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -179,13 +179,22 @@ when you upgrade the CLI. Pass `--global` to prefer it even when a local kit doe
179
179
  npx @eventmodelers/cli run --standalone --board-id <uuid> # from any directory, nothing written there
180
180
  ```
181
181
 
182
+ **Which board?** When `--board-id` isn't given, the agent asks for it on start, pre-filled
183
+ with whatever the config resolved to — press Enter to accept it, or paste a different board
184
+ id. A board inherited from a config file can be arbitrarily stale, and which board a run
185
+ drives is the one thing worth confirming. Skipped when there's no one to ask (`--print`, or a
186
+ non-interactive stdin), where the resolved value stands on its own.
187
+
182
188
  **Credentials are per board, not per directory.** The first time this machine runs a board it
183
- asks one question — does this board get credentials of its own, or does it use your
189
+ asks one more question — does this board get credentials of its own, or does it use your
184
190
  account-wide ones? Answer once and it's remembered: either the board's credentials or a
185
191
  `useGlobal` marker lands in `~/.eventmodelers/boards/<board>.json`, and you're not asked
186
- again. The question is skipped entirely when the answer is already implied (credentials given
187
- on the command line) or when there's no one to ask (`--print`, or a non-interactive stdin such
188
- as CI or a process supervisor).
192
+ again. If the credentials you paste name a *different* board than the one asked about an easy
193
+ way to end up there is a stale `boardId` in `~/.eventmodelers/config.json` the run switches
194
+ to the pasted board and leaves a pointer behind for the one it asked about, so the question is
195
+ asked once rather than on every start. The question is skipped entirely when the answer is
196
+ already implied (credentials given on the command line) or when there's no one to ask
197
+ (`--print`, or a non-interactive stdin such as CI or a process supervisor).
189
198
 
190
199
  To configure a board up front instead, paste the blob from
191
200
  [app.eventmodelers.ai/account](https://app.eventmodelers.ai/account):
package/cli.js CHANGED
@@ -349,7 +349,16 @@ function getSharedRl() {
349
349
  // called, hanging forever. Pulling from the iterator instead queues each line until
350
350
  // something asks for it, so nothing emitted ahead of time is ever lost between prompts.
351
351
  async function prompt(question = '') {
352
- getSharedRl();
352
+ const rl = getSharedRl();
353
+ // selectPrompt pauses stdin when it tears down its raw-mode keypress handler. That was
354
+ // invisible for as long as every menu came BEFORE the first prompt() — getSharedRl's
355
+ // createInterface resumes stdin on the way in, so a freshly built readline never noticed.
356
+ // Once a prompt runs first (the Board ID question), the interface already exists and is
357
+ // reused, so the next prompt after a menu waits forever on a stream nobody resumed: the
358
+ // event loop drains and node reports an unsettled top-level await instead of reading the
359
+ // line. Both calls are no-ops when nothing paused anything.
360
+ rl.resume();
361
+ process.stdin.resume();
353
362
  if (question) process.stdout.write(question);
354
363
  const { value, done } = await sharedRlLines.next();
355
364
  return (done ? '' : value).trim();
@@ -1345,13 +1354,17 @@ function boardCredentialsPath(boardId) {
1345
1354
  return join(GLOBAL_DIR, 'boards', `${boardId}.json`);
1346
1355
  }
1347
1356
 
1348
- // Two shapes, never mixed: the board's own credentials, or a note that this board just
1349
- // uses the account-wide ones. The second is what makes the first-run question a
1350
- // once-per-board event instead of something to dismiss on every start.
1357
+ // Three shapes, never mixed: the board's own credentials, a note that this board just uses
1358
+ // the account-wide ones, or a pointer to another board's file. All three exist for the same
1359
+ // reason — the first-run question has to be a once-per-board event rather than something to
1360
+ // dismiss on every start, so every possible answer has to be recordable against the board
1361
+ // that was ASKED about, including "actually, those credentials were for a different board".
1351
1362
  function writeBoardCredentials(config) {
1352
1363
  const path = boardCredentialsPath(config.boardId);
1353
1364
  mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
1354
- const body = config.useGlobal
1365
+ const body = config.useBoard
1366
+ ? { boardId: config.boardId, useBoard: config.useBoard }
1367
+ : config.useGlobal
1355
1368
  ? { boardId: config.boardId, useGlobal: true, agentId: config.agentId }
1356
1369
  : {
1357
1370
  token: config.token,
@@ -1407,8 +1420,36 @@ async function resolveModelingCredentials(cwd, flags, explicitConfigPath, print)
1407
1420
  // Which board comes first — everything else is stored per board, so there is nothing to
1408
1421
  // look up until we know which board this run is for.
1409
1422
  let boardId = explicit.boardId || process.env.EVENTMODELERS_BOARD_ID || walked.boardId || null;
1423
+
1424
+ // Which board a run drives is the single most consequential thing about it, and a boardId
1425
+ // inherited from a config file can be arbitrarily stale — so when it wasn't named on the
1426
+ // command line, confirm it. Enter accepts whatever the config resolved to, keeping the
1427
+ // common case to one keystroke. Skipped when there is no one to ask (--print, or a
1428
+ // non-interactive stdin such as CI or a process supervisor), where the resolved value
1429
+ // stands on its own exactly as before.
1430
+ let boardChosen = !!(explicit.boardId || process.env.EVENTMODELERS_BOARD_ID);
1431
+ if (!boardChosen && !print && process.stdin.isTTY) {
1432
+ const answer = await prompt(boardId ? `\n Board ID [${boardId}]: ` : '\n Board ID: ');
1433
+ if (answer) {
1434
+ boardId = answer;
1435
+ boardChosen = true;
1436
+ }
1437
+ }
1438
+
1410
1439
  let stored = boardId ? readJsonSafe(boardCredentialsPath(boardId)) : {};
1411
1440
 
1441
+ // Follow a pointer left by an earlier answer: this directory (or the account config) keeps
1442
+ // resolving to one board, but the credentials pasted for it named another. Without this the
1443
+ // question below would be re-asked on every single start, since the board that gets a file
1444
+ // is never the board the next run resolves. Not followed when the board was named
1445
+ // explicitly — that is a direct instruction, not an inherited default. One hop only: a
1446
+ // pointer always targets a board that then holds real credentials, so a chain would mean a
1447
+ // corrupted store rather than something to chase.
1448
+ if (stored.useBoard && !boardChosen) {
1449
+ boardId = stored.useBoard;
1450
+ stored = readJsonSafe(boardCredentialsPath(boardId));
1451
+ }
1452
+
1412
1453
  // First time this machine has seen this board, ask the one question that can't be
1413
1454
  // guessed: does it get credentials of its own, or does it ride on the account-wide ones?
1414
1455
  // The answer is recorded either way (as credentials, or as a useGlobal marker), so this
@@ -1439,8 +1480,15 @@ async function resolveModelingCredentials(cwd, flags, explicitConfigPath, print)
1439
1480
  console.error("\n❌ Couldn't make sense of that paste — nothing was saved.");
1440
1481
  process.exit(1);
1441
1482
  }
1442
- // The paste is the more specific answer about which board this is: someone who
1443
- // copied board B's credentials means board B, whatever the command line defaulted to.
1483
+ // The paste is the more specific answer about which board this is: someone who copied
1484
+ // board B's credentials means board B, whatever the command line defaulted to. But the
1485
+ // question was asked ABOUT board A, so board A needs an answer on file too — otherwise
1486
+ // the next run resolves A again, finds nothing, and asks all over again.
1487
+ if (parsed.boardId && boardId && parsed.boardId !== boardId) {
1488
+ writeBoardCredentials({ boardId, useBoard: parsed.boardId });
1489
+ console.log(`\n ℹ️ Those credentials are for board ${parsed.boardId}, not ${boardId} — noted, so this is asked once and not again.`);
1490
+ console.log(` Pass --board-id to pick a different board, or drop the stale boardId from ~/.eventmodelers/config.json.`);
1491
+ }
1444
1492
  if (parsed.boardId) boardId = parsed.boardId;
1445
1493
  stored = parsed;
1446
1494
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.57",
3
+ "version": "1.0.58",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, Cratis, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {