@wrongstack/cli 1.1.4 → 1.1.6

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 (103) hide show
  1. package/dist/{acp-MZ5MQBFN.js → acp-CZBSFQB7.js} +6 -5
  2. package/dist/acp-leader/acp-leader-confirm.d.ts +2 -0
  3. package/dist/acp-leader/acp-leader-host-binding.d.ts +1 -0
  4. package/dist/acp-leader/acp-leader-host.d.ts +9 -0
  5. package/dist/acp-leader/acp-leader-picker.d.ts +6 -3
  6. package/dist/acp-leader/acp-leader-provider.d.ts +10 -0
  7. package/dist/acp-leader/acp-leader-skills.d.ts +5 -0
  8. package/dist/acp-leader/acp-leader-stream.d.ts +9 -0
  9. package/dist/{auth-IHQS6AFW.js → auth-2ZG5DBHS.js} +4 -4
  10. package/dist/boot/launch-menu-arrow.d.ts +20 -0
  11. package/dist/boot/launch-menu-types.d.ts +36 -0
  12. package/dist/boot/launch-menu.d.ts +1 -57
  13. package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
  14. package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
  15. package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
  16. package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
  17. package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
  18. package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
  19. package/dist/{chunk-COGI7EQU.js → chunk-EAATL6FL.js} +713 -21
  20. package/dist/{chunk-AWRZWPJ3.js → chunk-FWUIKCCO.js} +152 -123
  21. package/dist/{chunk-TAE3EGBM.js → chunk-H5DCQFUI.js} +5 -5
  22. package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
  23. package/dist/chunk-LYWOV4OX.js +252 -0
  24. package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
  25. package/dist/{chunk-I5LJLKA3.js → chunk-MKMM5RIW.js} +12 -5
  26. package/dist/{chunk-ZF4HRR5F.js → chunk-P7MEEUGT.js} +275 -250
  27. package/dist/chunk-PGC44T7N.js +255 -0
  28. package/dist/chunk-QLYPIJ7X.js +258 -0
  29. package/dist/{chunk-JKTJSLUT.js → chunk-QRWXHGLB.js} +242 -18
  30. package/dist/chunk-RN5CFAVQ.js +292 -0
  31. package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
  32. package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
  33. package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
  34. package/dist/{chunk-2LTHDKZX.js → chunk-W7X23HI6.js} +18 -18
  35. package/dist/{chunk-GKGWPFW2.js → chunk-X4DEQLAZ.js} +1 -1
  36. package/dist/{cli-context-E44HQ4ZO.js → cli-context-6KS3HV5R.js} +247 -236
  37. package/dist/{cli-main-OT63I2FX.js → cli-main-NF3YGDME.js} +1779 -1494
  38. package/dist/cli-main-boot.d.ts +1 -1
  39. package/dist/config-doctor-known-keys.d.ts +33 -0
  40. package/dist/config-doctor.d.ts +2 -47
  41. package/dist/config-history-support.d.ts +36 -0
  42. package/dist/config-history.d.ts +1 -1
  43. package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
  44. package/dist/{execution-OK34CJKJ.js → execution-RVDUXLNT.js} +6522 -6426
  45. package/dist/execution-tui.d.ts +30 -0
  46. package/dist/fleet/acp-wrongstack-bridge.d.ts +11 -3
  47. package/dist/fleet/host-acp.d.ts +3 -0
  48. package/dist/fleet/wrongforge-client.d.ts +116 -0
  49. package/dist/fleet/wrongforge-gateway.d.ts +20 -0
  50. package/dist/fleet/wrongforge-info.d.ts +32 -0
  51. package/dist/fleet/wrongforge-quota.d.ts +10 -0
  52. package/dist/fleet/wrongforge-runner.d.ts +37 -0
  53. package/dist/fleet/wrongforge-runtime.d.ts +10 -0
  54. package/dist/{hq-JD5AZVMG.js → hq-V2M4IYCI.js} +320 -306
  55. package/dist/hq-server/login-attempt-store.d.ts +2 -0
  56. package/dist/hq-server/sweeps.d.ts +28 -0
  57. package/dist/{hq-server-BI23EPM7.js → hq-server-QM7EIVGJ.js} +3 -3
  58. package/dist/index.js +5 -5
  59. package/dist/mcp-22CFBX4H.js +236 -0
  60. package/dist/mcp-serve.d.ts +0 -63
  61. package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
  62. package/dist/{per-subcommand-help-4GYSCMLU.js → per-subcommand-help-MQGERD4E.js} +2 -2
  63. package/dist/plugin-llm-command.d.ts +9 -0
  64. package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
  65. package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
  66. package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
  67. package/dist/services/sdd/state.d.ts +2 -0
  68. package/dist/session-event-wiring-types.d.ts +62 -0
  69. package/dist/session-event-wiring.d.ts +2 -52
  70. package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
  71. package/dist/short-circuit-flags-276OLPIN.js +11 -0
  72. package/dist/slash-commands/command-context-fleet.d.ts +187 -0
  73. package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
  74. package/dist/slash-commands/command-context.d.ts +3 -282
  75. package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
  76. package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
  77. package/dist/subcommands/handlers/hq-token.d.ts +3 -0
  78. package/dist/subcommands/handlers/hq.d.ts +0 -21
  79. package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
  80. package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
  81. package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
  82. package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
  83. package/dist/{subcommands-YADUV7IJ.js → subcommands-SKJPUJ7N.js} +2 -2
  84. package/dist/tool-gateway-http.d.ts +30 -0
  85. package/dist/tool-gateway.d.ts +185 -0
  86. package/dist/tools-cli-44MLIUQO.js +509 -0
  87. package/dist/tools-serve-TRCET2M7.js +102 -0
  88. package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
  89. package/dist/tuneup-runtime-checks.d.ts +7 -0
  90. package/dist/tuneup-types.d.ts +118 -0
  91. package/dist/tuneup.d.ts +3 -122
  92. package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
  93. package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
  94. package/dist/{webui-server-CB64CFDG.js → webui-server-6I6QOECV.js} +44 -6
  95. package/dist/wiring/provider-runtime-setup.d.ts +3 -1
  96. package/dist/wiring/session-runtime.d.ts +1 -1
  97. package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
  98. package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
  99. package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
  100. package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +56 -0
  101. package/package.json +1 -1
  102. package/dist/mcp-W7A5HUXV.js +0 -560
  103. package/dist/short-circuit-flags-QEXUS3BY.js +0 -11
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  normalizeHqPublicOrigin,
9
9
  resolveAuditActor
10
- } from "./chunk-3EXRPTAU.js";
10
+ } from "./chunk-5TQ65PXQ.js";
11
11
  import {
12
12
  parseHqIpAllowlist
13
13
  } from "./chunk-OGVSKS4Q.js";
@@ -18,13 +18,8 @@ import {
18
18
  HQ_AUTH_FILE_VERSION,
19
19
  HQ_CLI_DEFAULT_HOST,
20
20
  HqInsecureExposureError,
21
- isLoopbackHost,
22
- logHqAuthAudit,
23
- mintHqToken,
24
- mutateHqAuthFile,
25
- readHqAuthFile as readHqAuthFile2
21
+ isLoopbackHost
26
22
  } from "@wrongstack/core/hq";
27
- import { expectDefined } from "@wrongstack/core/utils";
28
23
 
29
24
  // src/subcommands/handlers/hq-alerts.ts
30
25
  import * as fs from "node:fs/promises";
@@ -297,243 +292,14 @@ function printAuditHelp(deps) {
297
292
  `);
298
293
  }
299
294
 
300
- // src/subcommands/handlers/hq.ts
301
- var hqCmd = async (args, deps) => {
302
- const sub = args[0];
303
- if (deps.flags?.["help"] === true && (!sub || sub === "serve")) {
304
- printHelp(deps);
305
- return 0;
306
- }
307
- if (!sub || sub === "serve") {
308
- return startServer(deps);
309
- }
310
- if (sub === "token") {
311
- return hqTokenCmd(args.slice(1), deps);
312
- }
313
- if (sub === "audit") {
314
- return hqAuditCmd(args.slice(1), deps);
315
- }
316
- if (sub === "alerts") {
317
- return hqAlertsCmd(args.slice(1), deps);
318
- }
319
- if (sub === "service") {
320
- const { hqServiceCmd } = await import("./hq-service-TQNPIRHL.js");
321
- return hqServiceCmd(args.slice(1), deps);
322
- }
323
- if (sub === "help" || sub === "--help") {
324
- printHelp(deps);
325
- return 0;
326
- }
327
- deps.renderer.writeError(`Unknown hq subcommand: ${sub}
328
- `);
329
- printHelp(deps);
330
- return 1;
331
- };
332
- async function startServer(deps) {
333
- const { startHqServer } = await import("./hq-server-BI23EPM7.js");
334
- const dataDir = resolveDataDir(deps);
335
- const flags = deps.flags ?? {};
336
- const rawPublicUrl = typeof flags["hq-public-url"] === "string" ? flags["hq-public-url"] : process.env.WRONGSTACK_HQ_PUBLIC_URL;
337
- let publicOrigin;
338
- if (rawPublicUrl !== void 0 && rawPublicUrl.trim() !== "") {
339
- try {
340
- publicOrigin = normalizeHqPublicOrigin(rawPublicUrl);
341
- } catch (cause) {
342
- deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
343
- `);
344
- return 1;
345
- }
346
- }
347
- const host = typeof flags["host"] === "string" ? flags["host"] : publicOrigin !== void 0 ? "127.0.0.1" : HQ_CLI_DEFAULT_HOST;
348
- const rawPort = typeof flags["port"] === "string" ? flags["port"].trim() : void 0;
349
- const port = rawPort === void 0 ? 3499 : /^\d+$/.test(rawPort) ? Number(rawPort) : Number.NaN;
350
- if (!Number.isInteger(port) || port < 0 || port > 65535) {
351
- deps.renderer.writeError(`Invalid --port value: ${String(flags["port"])}
352
- `);
353
- return 1;
354
- }
355
- if (publicOrigin !== void 0 && !isLoopbackHost(host)) {
356
- deps.renderer.writeError(
357
- "Persistent public HQ requires a loopback bind. Remove --host or use --host 127.0.0.1.\n"
358
- );
359
- return 1;
360
- }
361
- const strictPort = flags["strict-port"] === true;
362
- const open = flags["open"] === true;
363
- let password;
364
- try {
365
- password = await resolveHqPasswordInput({
366
- ...typeof flags["password"] === "string" ? { explicitPassword: flags["password"] } : {}
367
- });
368
- } catch (cause) {
369
- deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
370
- `);
371
- return 1;
372
- }
373
- const bootstrapPasswordOnly = /^(?:1|true)$/i.test(
374
- process.env.WRONGSTACK_HQ_BOOTSTRAP_PASSWORD_ONLY?.trim() ?? ""
375
- );
376
- const allowInsecureOpen = flags["insecure-open"] === true;
377
- const rawAllowlist = typeof flags["hq-allowlist"] === "string" ? flags["hq-allowlist"] : process.env.WRONGSTACK_HQ_ALLOWLIST;
378
- let ipAllowlist;
379
- try {
380
- ipAllowlist = parseHqIpAllowlist(rawAllowlist);
381
- } catch (cause) {
382
- deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
383
- `);
384
- return 1;
385
- }
386
- const rawTtl = typeof flags["hq-token-ttl"] === "string" ? flags["hq-token-ttl"] : void 0;
387
- let tokenTtlMs;
388
- if (rawTtl !== void 0 && rawTtl.length > 0) {
389
- const parsed = parseTokenTtlValue(rawTtl);
390
- if (parsed.error) {
391
- deps.renderer.writeError(`Invalid --hq-token-ttl: ${parsed.error}
392
- `);
393
- return 1;
394
- }
395
- tokenTtlMs = parsed.value;
396
- }
397
- const rawHops = typeof flags["hq-trusted-proxy-hops"] === "string" ? flags["hq-trusted-proxy-hops"] : void 0;
398
- let trustedProxyHops;
399
- if (rawHops !== void 0 && rawHops.length > 0) {
400
- const parsed = Number.parseInt(rawHops, 10);
401
- if (!Number.isFinite(parsed) || parsed < 0 || String(parsed) !== rawHops.trim()) {
402
- deps.renderer.writeError(
403
- "--hq-trusted-proxy-hops must be a non-negative integer (0 = ignore X-Forwarded-For).\n"
404
- );
405
- return 1;
406
- }
407
- trustedProxyHops = parsed;
408
- }
409
- if (password !== void 0 && password.length < 8) {
410
- deps.renderer.writeError("HQ password must be at least 8 characters.\n");
411
- return 1;
412
- }
413
- let handle;
414
- try {
415
- handle = await startHqServer({
416
- host,
417
- port,
418
- strictPort,
419
- dataDir,
420
- allowInsecureOpen,
421
- secureCookies: publicOrigin !== void 0,
422
- requireBrowserAuth: publicOrigin !== void 0,
423
- ...password !== void 0 ? { password } : {},
424
- ...bootstrapPasswordOnly ? { bootstrapPasswordOnly: true } : {},
425
- ...tokenTtlMs !== void 0 ? { tokenTtlMs } : {},
426
- ...trustedProxyHops !== void 0 ? { trustedProxyHops } : {},
427
- ...ipAllowlist !== void 0 ? { ipAllowlist } : {}
428
- });
429
- } catch (err) {
430
- if (err instanceof HqInsecureExposureError) {
431
- deps.renderer.writeError(`${err.message}
432
- `);
433
- return 1;
434
- }
435
- throw err;
436
- }
437
- if (publicOrigin !== void 0) {
438
- if (handle.firstRunSetup?.passwordMode !== true) {
439
- await handle.close();
440
- deps.renderer.writeError(
441
- "Persistent public HQ requires password mode. Set WRONGSTACK_HQ_PASSWORD and retry.\n"
442
- );
443
- return 1;
444
- }
445
- handle.trustPublicOrigin(publicOrigin);
446
- deps.renderer.write(`Public endpoint: ${publicOrigin}/
447
- `);
448
- deps.renderer.write(`Public mobile: ${publicOrigin}/mobile
449
- `);
450
- }
451
- if (open) {
452
- try {
453
- const { openBrowser } = await import("@wrongstack/webui-server");
454
- openBrowser(
455
- publicOrigin ?? handle.firstRunSetup?.browserUrl ?? `http://${handle.host}:${handle.port}`
456
- );
457
- } catch {
458
- }
459
- }
460
- writeStartupInfo(deps, handle);
461
- deps.renderer.write(
462
- ipAllowlist === void 0 ? "Network allowlist: disabled (authentication remains required when configured).\n" : `Network allowlist: active (${ipAllowlist.length} configured rule${ipAllowlist.length === 1 ? "" : "s"} + loopback).
463
- `
464
- );
465
- await new Promise((resolve2) => {
466
- const shutdown = async () => {
467
- try {
468
- await handle.close();
469
- } catch (err) {
470
- deps.renderer.write(`HQ server close error: ${String(err)}
471
- `);
472
- }
473
- resolve2();
474
- };
475
- process.on("SIGINT", shutdown);
476
- process.on("SIGTERM", shutdown);
477
- });
478
- return 0;
479
- }
480
- function writeStartupInfo(deps, handle) {
481
- deps.renderer.write(`WrongStack HQ listening on http://${handle.host}:${handle.port}
482
- `);
483
- if (!handle.firstRunSetup) {
484
- deps.renderer.write(`Client endpoint: ws://${handle.host}:${handle.port}/ws/client
485
- `);
486
- deps.renderer.write(`Browser endpoint: http://${handle.host}:${handle.port}
487
- `);
488
- deps.renderer.write(
489
- `Mobile endpoint: http://${handle.host}:${handle.port}/mobile (password required)
490
- `
491
- );
492
- return;
493
- }
494
- if (process.env.WRONGSTACK_HQ_SUPPRESS_STARTUP_SECRETS === "1") {
495
- deps.renderer.write(`Browser endpoint: http://${handle.host}:${handle.port}
496
- `);
497
- deps.renderer.write(
498
- `Mobile endpoint: http://${handle.host}:${handle.port}/mobile (password required)
499
- `
500
- );
501
- deps.renderer.write(`Client endpoint: ws://${handle.host}:${handle.port}/ws/client
502
- `);
503
- deps.renderer.write(
504
- `Startup credentials suppressed; auth state is stored in ${handle.firstRunSetup.dataDir}.
505
- `
506
- );
507
- return;
508
- }
509
- const mobileUrl = new URL(handle.firstRunSetup.browserUrl);
510
- mobileUrl.pathname = "/mobile";
511
- mobileUrl.search = "";
512
- mobileUrl.hash = "";
513
- deps.renderer.write(`Browser endpoint: ${handle.firstRunSetup.browserUrl}
514
- `);
515
- deps.renderer.write(`Mobile endpoint: ${mobileUrl.toString()} (password required)
516
- `);
517
- deps.renderer.write(`Client endpoint: ${handle.firstRunSetup.clientUrl}
518
- `);
519
- deps.renderer.write(
520
- handle.firstRunSetup.createdAuth ? `
521
- First-run HQ auth created in ${handle.firstRunSetup.dataDir}
522
- ` : `
523
- HQ auth loaded from ${handle.firstRunSetup.dataDir}
524
- `
525
- );
526
- deps.renderer.write(`Start clients with:
527
- `);
528
- deps.renderer.write(` WRONGSTACK_HQ_URL=${handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_URL}
529
- `);
530
- if (handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_TOKEN) {
531
- deps.renderer.write(
532
- ` WRONGSTACK_HQ_TOKEN=${handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_TOKEN}
533
- `
534
- );
535
- }
536
- }
295
+ // src/subcommands/handlers/hq-token.ts
296
+ import {
297
+ logHqAuthAudit,
298
+ mintHqToken,
299
+ mutateHqAuthFile,
300
+ readHqAuthFile as readHqAuthFile2
301
+ } from "@wrongstack/core/hq";
302
+ import { expectDefined } from "@wrongstack/core/utils";
537
303
  async function hqTokenCmd(args, deps) {
538
304
  const action = args[0];
539
305
  if (deps.flags?.["help"] === true || action === "help" || action === "--help") {
@@ -786,18 +552,21 @@ async function tokenRevoke(args, deps) {
786
552
  const dataDir = resolveDataDir(deps);
787
553
  const tokenField = scope === "client" ? "clientTokens" : "browserTokens";
788
554
  let revoked;
555
+ let ambiguous = [];
789
556
  try {
790
557
  await mutateHqAuthFile(
791
558
  dataDir,
792
559
  (current) => {
793
560
  const tokens = current[tokenField] ?? [];
794
561
  const matches = tokens.filter((t) => t.id.startsWith(idPrefix));
562
+ ambiguous = [];
795
563
  if (matches.length === 0) {
796
564
  revoked = void 0;
797
565
  return current;
798
566
  }
799
567
  if (matches.length > 1) {
800
- revoked = matches[0];
568
+ revoked = void 0;
569
+ ambiguous = matches;
801
570
  return current;
802
571
  }
803
572
  revoked = matches[0];
@@ -814,6 +583,13 @@ async function tokenRevoke(args, deps) {
814
583
  `);
815
584
  return 1;
816
585
  }
586
+ if (ambiguous.length > 0) {
587
+ deps.renderer.writeError(
588
+ `Id-prefix "${idPrefix}" matches ${ambiguous.length} ${scope} tokens (${ambiguous.map((t) => t.id).join(", ")}); nothing revoked. Use a longer prefix.
589
+ `
590
+ );
591
+ return 1;
592
+ }
817
593
  if (!revoked) {
818
594
  deps.renderer.writeError(`No ${scope} token found matching id-prefix "${idPrefix}".
819
595
  `);
@@ -846,8 +622,304 @@ async function tokenRevoke(args, deps) {
846
622
  );
847
623
  return 0;
848
624
  }
849
- function printHelp(deps) {
850
- deps.renderer.write(`Usage: wstack hq <serve | token | audit | service>
625
+ function printTokenHelp(deps) {
626
+ deps.renderer.write(`Usage: wstack hq token <create | list | revoke>
627
+ `);
628
+ deps.renderer.write("\n");
629
+ deps.renderer.write(
630
+ ` wstack hq token create [label] [--ttl <dur>] Mint a browser token (enters token mode).
631
+ `
632
+ );
633
+ deps.renderer.write(
634
+ ` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
635
+ `
636
+ );
637
+ deps.renderer.write(
638
+ ` wstack hq token list List issued browser tokens (alias: ls).
639
+ `
640
+ );
641
+ deps.renderer.write(
642
+ ` wstack hq token list --client List issued client tokens.
643
+ `
644
+ );
645
+ deps.renderer.write(
646
+ ` wstack hq token revoke <id> Revoke a browser token (id prefix match).
647
+ `
648
+ );
649
+ deps.renderer.write(
650
+ ` wstack hq token revoke --client <id> Revoke a client token.
651
+ `
652
+ );
653
+ deps.renderer.write("\n");
654
+ deps.renderer.write(`The token secret is printed once at create time and cannot be recovered
655
+ `);
656
+ deps.renderer.write(`from auth.json (only its SHA-256 verifier is persisted). Tokens live in
657
+ `);
658
+ deps.renderer.write(`<dataDir>/auth.json (default ~/.wrongstack/hq/auth.json).
659
+ `);
660
+ deps.renderer.write("\n");
661
+ deps.renderer.write(`Flags:
662
+ `);
663
+ deps.renderer.write(
664
+ ` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
665
+ `
666
+ );
667
+ deps.renderer.write(
668
+ ` --client, -c Operate on client tokens instead of browser tokens.
669
+ `
670
+ );
671
+ deps.renderer.write(
672
+ ` --capabilities <csv> Comma-separated capability grants (browser: control.enqueue,control.approve; client: telemetry.publish,control.execute).
673
+ `
674
+ );
675
+ deps.renderer.write(
676
+ ` --ttl <duration> Stamp an expiresAt on the token (e.g. --ttl 1h, --ttl 7d, --ttl 3600s).
677
+ `
678
+ );
679
+ deps.renderer.write("\n");
680
+ deps.renderer.write(`Run \`wstack hq --help\` for the full HQ command list.
681
+ `);
682
+ }
683
+
684
+ // src/subcommands/handlers/hq.ts
685
+ var hqCmd = async (args, deps) => {
686
+ const sub = args[0];
687
+ if (deps.flags?.["help"] === true && (!sub || sub === "serve")) {
688
+ printHelp(deps);
689
+ return 0;
690
+ }
691
+ if (!sub || sub === "serve") {
692
+ return startServer(deps);
693
+ }
694
+ if (sub === "token") {
695
+ return hqTokenCmd(args.slice(1), deps);
696
+ }
697
+ if (sub === "audit") {
698
+ return hqAuditCmd(args.slice(1), deps);
699
+ }
700
+ if (sub === "alerts") {
701
+ return hqAlertsCmd(args.slice(1), deps);
702
+ }
703
+ if (sub === "service") {
704
+ const { hqServiceCmd } = await import("./hq-service-TQNPIRHL.js");
705
+ return hqServiceCmd(args.slice(1), deps);
706
+ }
707
+ if (sub === "help" || sub === "--help") {
708
+ printHelp(deps);
709
+ return 0;
710
+ }
711
+ deps.renderer.writeError(`Unknown hq subcommand: ${sub}
712
+ `);
713
+ printHelp(deps);
714
+ return 1;
715
+ };
716
+ async function startServer(deps) {
717
+ const { startHqServer } = await import("./hq-server-QM7EIVGJ.js");
718
+ const dataDir = resolveDataDir(deps);
719
+ const flags = deps.flags ?? {};
720
+ const rawPublicUrl = typeof flags["hq-public-url"] === "string" ? flags["hq-public-url"] : process.env.WRONGSTACK_HQ_PUBLIC_URL;
721
+ let publicOrigin;
722
+ if (rawPublicUrl !== void 0 && rawPublicUrl.trim() !== "") {
723
+ try {
724
+ publicOrigin = normalizeHqPublicOrigin(rawPublicUrl);
725
+ } catch (cause) {
726
+ deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
727
+ `);
728
+ return 1;
729
+ }
730
+ }
731
+ const host = typeof flags["host"] === "string" ? flags["host"] : publicOrigin !== void 0 ? "127.0.0.1" : HQ_CLI_DEFAULT_HOST;
732
+ const rawPort = typeof flags["port"] === "string" ? flags["port"].trim() : void 0;
733
+ const port = rawPort === void 0 ? 3499 : /^\d+$/.test(rawPort) ? Number(rawPort) : Number.NaN;
734
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
735
+ deps.renderer.writeError(`Invalid --port value: ${String(flags["port"])}
736
+ `);
737
+ return 1;
738
+ }
739
+ if (publicOrigin !== void 0 && !isLoopbackHost(host)) {
740
+ deps.renderer.writeError(
741
+ "Persistent public HQ requires a loopback bind. Remove --host or use --host 127.0.0.1.\n"
742
+ );
743
+ return 1;
744
+ }
745
+ const strictPort = flags["strict-port"] === true;
746
+ const open = flags["open"] === true;
747
+ let password;
748
+ try {
749
+ password = await resolveHqPasswordInput({
750
+ ...typeof flags["password"] === "string" ? { explicitPassword: flags["password"] } : {}
751
+ });
752
+ } catch (cause) {
753
+ deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
754
+ `);
755
+ return 1;
756
+ }
757
+ const bootstrapPasswordOnly = /^(?:1|true)$/i.test(
758
+ process.env.WRONGSTACK_HQ_BOOTSTRAP_PASSWORD_ONLY?.trim() ?? ""
759
+ );
760
+ const allowInsecureOpen = flags["insecure-open"] === true;
761
+ const rawAllowlist = typeof flags["hq-allowlist"] === "string" ? flags["hq-allowlist"] : process.env.WRONGSTACK_HQ_ALLOWLIST;
762
+ let ipAllowlist;
763
+ try {
764
+ ipAllowlist = parseHqIpAllowlist(rawAllowlist);
765
+ } catch (cause) {
766
+ deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
767
+ `);
768
+ return 1;
769
+ }
770
+ const rawTtl = typeof flags["hq-token-ttl"] === "string" ? flags["hq-token-ttl"] : void 0;
771
+ let tokenTtlMs;
772
+ if (rawTtl !== void 0 && rawTtl.length > 0) {
773
+ const parsed = parseTokenTtlValue(rawTtl);
774
+ if (parsed.error) {
775
+ deps.renderer.writeError(`Invalid --hq-token-ttl: ${parsed.error}
776
+ `);
777
+ return 1;
778
+ }
779
+ tokenTtlMs = parsed.value;
780
+ }
781
+ const rawHops = typeof flags["hq-trusted-proxy-hops"] === "string" ? flags["hq-trusted-proxy-hops"] : void 0;
782
+ let trustedProxyHops;
783
+ if (rawHops !== void 0 && rawHops.length > 0) {
784
+ const parsed = Number.parseInt(rawHops, 10);
785
+ if (!Number.isFinite(parsed) || parsed < 0 || String(parsed) !== rawHops.trim()) {
786
+ deps.renderer.writeError(
787
+ "--hq-trusted-proxy-hops must be a non-negative integer (0 = ignore X-Forwarded-For).\n"
788
+ );
789
+ return 1;
790
+ }
791
+ trustedProxyHops = parsed;
792
+ }
793
+ if (password !== void 0 && password.length < 8) {
794
+ deps.renderer.writeError("HQ password must be at least 8 characters.\n");
795
+ return 1;
796
+ }
797
+ let handle;
798
+ try {
799
+ handle = await startHqServer({
800
+ host,
801
+ port,
802
+ strictPort,
803
+ dataDir,
804
+ allowInsecureOpen,
805
+ secureCookies: publicOrigin !== void 0,
806
+ requireBrowserAuth: publicOrigin !== void 0,
807
+ ...password !== void 0 ? { password } : {},
808
+ ...bootstrapPasswordOnly ? { bootstrapPasswordOnly: true } : {},
809
+ ...tokenTtlMs !== void 0 ? { tokenTtlMs } : {},
810
+ ...trustedProxyHops !== void 0 ? { trustedProxyHops } : {},
811
+ ...ipAllowlist !== void 0 ? { ipAllowlist } : {}
812
+ });
813
+ } catch (err) {
814
+ if (err instanceof HqInsecureExposureError) {
815
+ deps.renderer.writeError(`${err.message}
816
+ `);
817
+ return 1;
818
+ }
819
+ throw err;
820
+ }
821
+ if (publicOrigin !== void 0) {
822
+ if (handle.firstRunSetup?.passwordMode !== true) {
823
+ await handle.close();
824
+ deps.renderer.writeError(
825
+ "Persistent public HQ requires password mode. Set WRONGSTACK_HQ_PASSWORD and retry.\n"
826
+ );
827
+ return 1;
828
+ }
829
+ handle.trustPublicOrigin(publicOrigin);
830
+ deps.renderer.write(`Public endpoint: ${publicOrigin}/
831
+ `);
832
+ deps.renderer.write(`Public mobile: ${publicOrigin}/mobile
833
+ `);
834
+ }
835
+ if (open) {
836
+ try {
837
+ const { openBrowser } = await import("@wrongstack/webui-server");
838
+ openBrowser(
839
+ publicOrigin ?? handle.firstRunSetup?.browserUrl ?? `http://${handle.host}:${handle.port}`
840
+ );
841
+ } catch {
842
+ }
843
+ }
844
+ writeStartupInfo(deps, handle);
845
+ deps.renderer.write(
846
+ ipAllowlist === void 0 ? "Network allowlist: disabled (authentication remains required when configured).\n" : `Network allowlist: active (${ipAllowlist.length} configured rule${ipAllowlist.length === 1 ? "" : "s"} + loopback).
847
+ `
848
+ );
849
+ await new Promise((resolve2) => {
850
+ const shutdown = async () => {
851
+ try {
852
+ await handle.close();
853
+ } catch (err) {
854
+ deps.renderer.write(`HQ server close error: ${String(err)}
855
+ `);
856
+ }
857
+ resolve2();
858
+ };
859
+ process.on("SIGINT", shutdown);
860
+ process.on("SIGTERM", shutdown);
861
+ });
862
+ return 0;
863
+ }
864
+ function writeStartupInfo(deps, handle) {
865
+ deps.renderer.write(`WrongStack HQ listening on http://${handle.host}:${handle.port}
866
+ `);
867
+ if (!handle.firstRunSetup) {
868
+ deps.renderer.write(`Client endpoint: ws://${handle.host}:${handle.port}/ws/client
869
+ `);
870
+ deps.renderer.write(`Browser endpoint: http://${handle.host}:${handle.port}
871
+ `);
872
+ deps.renderer.write(
873
+ `Mobile endpoint: http://${handle.host}:${handle.port}/mobile (password required)
874
+ `
875
+ );
876
+ return;
877
+ }
878
+ if (process.env.WRONGSTACK_HQ_SUPPRESS_STARTUP_SECRETS === "1") {
879
+ deps.renderer.write(`Browser endpoint: http://${handle.host}:${handle.port}
880
+ `);
881
+ deps.renderer.write(
882
+ `Mobile endpoint: http://${handle.host}:${handle.port}/mobile (password required)
883
+ `
884
+ );
885
+ deps.renderer.write(`Client endpoint: ws://${handle.host}:${handle.port}/ws/client
886
+ `);
887
+ deps.renderer.write(
888
+ `Startup credentials suppressed; auth state is stored in ${handle.firstRunSetup.dataDir}.
889
+ `
890
+ );
891
+ return;
892
+ }
893
+ const mobileUrl = new URL(handle.firstRunSetup.browserUrl);
894
+ mobileUrl.pathname = "/mobile";
895
+ mobileUrl.search = "";
896
+ mobileUrl.hash = "";
897
+ deps.renderer.write(`Browser endpoint: ${handle.firstRunSetup.browserUrl}
898
+ `);
899
+ deps.renderer.write(`Mobile endpoint: ${mobileUrl.toString()} (password required)
900
+ `);
901
+ deps.renderer.write(`Client endpoint: ${handle.firstRunSetup.clientUrl}
902
+ `);
903
+ deps.renderer.write(
904
+ handle.firstRunSetup.createdAuth ? `
905
+ First-run HQ auth created in ${handle.firstRunSetup.dataDir}
906
+ ` : `
907
+ HQ auth loaded from ${handle.firstRunSetup.dataDir}
908
+ `
909
+ );
910
+ deps.renderer.write(`Start clients with:
911
+ `);
912
+ deps.renderer.write(` WRONGSTACK_HQ_URL=${handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_URL}
913
+ `);
914
+ if (handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_TOKEN) {
915
+ deps.renderer.write(
916
+ ` WRONGSTACK_HQ_TOKEN=${handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_TOKEN}
917
+ `
918
+ );
919
+ }
920
+ }
921
+ function printHelp(deps) {
922
+ deps.renderer.write(`Usage: wstack hq <serve | token | audit | service>
851
923
  `);
852
924
  deps.renderer.write("\n");
853
925
  deps.renderer.write(` wstack hq Start the HQ command center server.
@@ -949,65 +1021,7 @@ function printHelp(deps) {
949
1021
  deps.renderer.write(`auth.json schema version: ${HQ_AUTH_FILE_VERSION}.
950
1022
  `);
951
1023
  }
952
- function printTokenHelp(deps) {
953
- deps.renderer.write(`Usage: wstack hq token <create | list | revoke>
954
- `);
955
- deps.renderer.write("\n");
956
- deps.renderer.write(
957
- ` wstack hq token create [label] [--ttl <dur>] Mint a browser token (enters token mode).
958
- `
959
- );
960
- deps.renderer.write(
961
- ` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
962
- `
963
- );
964
- deps.renderer.write(
965
- ` wstack hq token list List issued browser tokens (alias: ls).
966
- `
967
- );
968
- deps.renderer.write(
969
- ` wstack hq token list --client List issued client tokens.
970
- `
971
- );
972
- deps.renderer.write(
973
- ` wstack hq token revoke <id> Revoke a browser token (id prefix match).
974
- `
975
- );
976
- deps.renderer.write(
977
- ` wstack hq token revoke --client <id> Revoke a client token.
978
- `
979
- );
980
- deps.renderer.write("\n");
981
- deps.renderer.write(`The token secret is printed once at create time and cannot be recovered
982
- `);
983
- deps.renderer.write(`from auth.json (only its SHA-256 verifier is persisted). Tokens live in
984
- `);
985
- deps.renderer.write(`<dataDir>/auth.json (default ~/.wrongstack/hq/auth.json).
986
- `);
987
- deps.renderer.write("\n");
988
- deps.renderer.write(`Flags:
989
- `);
990
- deps.renderer.write(
991
- ` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
992
- `
993
- );
994
- deps.renderer.write(
995
- ` --client, -c Operate on client tokens instead of browser tokens.
996
- `
997
- );
998
- deps.renderer.write(
999
- ` --capabilities <csv> Comma-separated capability grants (browser: control.enqueue,control.approve; client: telemetry.publish,control.execute).
1000
- `
1001
- );
1002
- deps.renderer.write(
1003
- ` --ttl <duration> Stamp an expiresAt on the token (e.g. --ttl 1h, --ttl 7d, --ttl 3600s).
1004
- `
1005
- );
1006
- deps.renderer.write("\n");
1007
- deps.renderer.write(`Run \`wstack hq --help\` for the full HQ command list.
1008
- `);
1009
- }
1010
1024
  export {
1011
1025
  hqCmd
1012
1026
  };
1013
- //# sourceMappingURL=hq-JD5AZVMG.js.map
1027
+ //# sourceMappingURL=hq-V2M4IYCI.js.map
@@ -22,6 +22,7 @@ export declare class LoginAttemptStore {
22
22
  private store;
23
23
  private writeTimer;
24
24
  private dirty;
25
+ private writeTail;
25
26
  private readonly filePath;
26
27
  private readonly debounceMs;
27
28
  private readonly retentionMs;
@@ -92,6 +93,7 @@ export declare class LoginAttemptStore {
92
93
  flush(): Promise<void>;
93
94
  private scheduleWrite;
94
95
  private writeToDisk;
96
+ private writeSnapshotToDisk;
95
97
  /** Iterate entries (for cleanup timers). */
96
98
  entries(): IterableIterator<[string, LoginAttemptEntry]>;
97
99
  }