@wrongstack/cli 1.1.2 → 1.1.5

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 (117) hide show
  1. package/dist/{acp-HCGUTFNE.js → acp-NKUEYJDZ.js} +14 -5
  2. package/dist/acp-leader/acp-leader-confirm.d.ts +20 -0
  3. package/dist/acp-leader/acp-leader-context.d.ts +29 -0
  4. package/dist/acp-leader/acp-leader-fleet.d.ts +26 -0
  5. package/dist/acp-leader/acp-leader-host-binding.d.ts +17 -0
  6. package/dist/acp-leader/acp-leader-host.d.ts +82 -0
  7. package/dist/acp-leader/acp-leader-picker.d.ts +24 -0
  8. package/dist/acp-leader/acp-leader-provider.d.ts +85 -0
  9. package/dist/acp-leader/acp-leader-resume.d.ts +10 -0
  10. package/dist/acp-leader/acp-leader-stream.d.ts +83 -0
  11. package/dist/{auth-ANOVZVHE.js → auth-2ZG5DBHS.js} +62 -66
  12. package/dist/auth-menu/index.d.ts +1 -2
  13. package/dist/boot/dispatch-webui.d.ts +16 -0
  14. package/dist/boot/launch-menu-arrow.d.ts +20 -0
  15. package/dist/boot/launch-menu-types.d.ts +36 -0
  16. package/dist/boot/launch-menu.d.ts +1 -57
  17. package/dist/boot/tui-session-resume-rollback.d.ts +31 -0
  18. package/dist/{boot-auto-thin-7D4OVEWA.js → boot-auto-thin-N2CIGTXE.js} +3 -3
  19. package/dist/{chunk-TZPKOS5G.js → chunk-2XQ6SRKG.js} +2 -2
  20. package/dist/chunk-3LEIFO5D.js +65 -0
  21. package/dist/chunk-5SGMUDP5.js +752 -0
  22. package/dist/{chunk-3EXRPTAU.js → chunk-5TQ65PXQ.js} +17 -17
  23. package/dist/{chunk-GRAGMA4B.js → chunk-7BM4LGKQ.js} +18 -18
  24. package/dist/{chunk-VAQXOZKP.js → chunk-AJM4EFCM.js} +139 -136
  25. package/dist/{chunk-C2BBH4S4.js → chunk-E3MJFAFR.js} +2 -2
  26. package/dist/{chunk-5TRZZHMN.js → chunk-JCZ2D3VV.js} +4 -65
  27. package/dist/{chunk-AWRZWPJ3.js → chunk-JFYQGMMM.js} +99 -75
  28. package/dist/{chunk-BIGSVBPI.js → chunk-KNYIEQAY.js} +64 -61
  29. package/dist/chunk-LUEO5HLZ.js +50 -0
  30. package/dist/chunk-LYWOV4OX.js +252 -0
  31. package/dist/chunk-M7FNHCE3.js +2070 -0
  32. package/dist/{chunk-75OLD563.js → chunk-M7PSENGF.js} +3 -1
  33. package/dist/{chunk-NMXQFQ26.js → chunk-MEBXWI7U.js} +5 -5
  34. package/dist/{chunk-RAWHRRUJ.js → chunk-MKMM5RIW.js} +209 -201
  35. package/dist/{chunk-3R3OADC5.js → chunk-P7MEEUGT.js} +281 -252
  36. package/dist/chunk-QLYPIJ7X.js +258 -0
  37. package/dist/chunk-RN5CFAVQ.js +292 -0
  38. package/dist/{chunk-WDXNBYGD.js → chunk-TDP7CZGE.js} +6 -1
  39. package/dist/{chunk-6VPWQ4TG.js → chunk-UROB7MK5.js} +2 -1
  40. package/dist/chunk-VEESMPTX.js +254 -0
  41. package/dist/{chunk-IJLIAPCW.js → chunk-VPSAKGLK.js} +47 -47
  42. package/dist/{cli-context-YIT4FEDM.js → cli-context-CQYGIWOU.js} +254 -237
  43. package/dist/{cli-main-JIADJ5OL.js → cli-main-VRUJNUBX.js} +2081 -1594
  44. package/dist/cli-main-boot.d.ts +1 -1
  45. package/dist/config-doctor-known-keys.d.ts +33 -0
  46. package/dist/config-doctor.d.ts +2 -47
  47. package/dist/config-history-support.d.ts +36 -0
  48. package/dist/config-history.d.ts +1 -1
  49. package/dist/{config-transfer-Y7XJMJRN.js → config-transfer-B4GOZTFC.js} +2 -2
  50. package/dist/{execution-BAP566QC.js → execution-IV72RUSM.js} +5354 -5258
  51. package/dist/execution-tui.d.ts +30 -0
  52. package/dist/fleet/acp-live-steer.d.ts +28 -0
  53. package/dist/fleet/acp-permission-bridge.d.ts +49 -0
  54. package/dist/fleet/acp-quota-hold.d.ts +3 -0
  55. package/dist/fleet/acp-quota-reading.d.ts +3 -0
  56. package/dist/fleet/acp-turn-reminders.d.ts +15 -0
  57. package/dist/fleet/acp-wrongstack-bridge.d.ts +50 -0
  58. package/dist/fleet/host-acp.d.ts +23 -2
  59. package/dist/fleet/wrongforge-client.d.ts +116 -0
  60. package/dist/fleet/wrongforge-gateway.d.ts +20 -0
  61. package/dist/fleet/wrongforge-info.d.ts +32 -0
  62. package/dist/fleet/wrongforge-quota.d.ts +10 -0
  63. package/dist/fleet/wrongforge-runner.d.ts +37 -0
  64. package/dist/fleet/wrongforge-runtime.d.ts +10 -0
  65. package/dist/{hq-JD5AZVMG.js → hq-54PQYR5O.js} +311 -307
  66. package/dist/hq-server/sweeps.d.ts +28 -0
  67. package/dist/{hq-server-BI23EPM7.js → hq-server-YK7FBLL6.js} +3 -3
  68. package/dist/index.js +8 -5
  69. package/dist/mcp-22CFBX4H.js +236 -0
  70. package/dist/mcp-serve-project.d.ts +18 -0
  71. package/dist/mcp-serve.d.ts +0 -63
  72. package/dist/{modeldiag-HDIKG37X.js → modeldiag-5PBB777J.js} +4 -4
  73. package/dist/{per-subcommand-help-D6J4ZXW2.js → per-subcommand-help-MQGERD4E.js} +2 -2
  74. package/dist/plugin-llm-command.d.ts +9 -0
  75. package/dist/{plugin-usage-TYF5HY4K.js → plugin-usage-UWKYMNQU.js} +3 -3
  76. package/dist/{plugins-XGAXDLPT.js → plugins-VP7JQH47.js} +4 -4
  77. package/dist/{sdd-runtime-6MFUXWJK.js → sdd-runtime-HIJ4Z5TF.js} +2 -2
  78. package/dist/services/sdd/state.d.ts +2 -0
  79. package/dist/session-event-wiring-types.d.ts +62 -0
  80. package/dist/session-event-wiring.d.ts +2 -52
  81. package/dist/{sessions-config-SJVQXCIE.js → sessions-config-7MK742OW.js} +2 -2
  82. package/dist/short-circuit-flags-AGHURHMQ.js +11 -0
  83. package/dist/slash-commands/command-context-fleet.d.ts +187 -0
  84. package/dist/slash-commands/command-context-sdd-goal.d.ts +102 -0
  85. package/dist/slash-commands/command-context.d.ts +3 -282
  86. package/dist/slash-commands/project-switch-interactive.d.ts +11 -0
  87. package/dist/slash-commands/setmodel-helpers.d.ts +36 -0
  88. package/dist/subcommands/handlers/hq-token.d.ts +3 -0
  89. package/dist/subcommands/handlers/hq.d.ts +0 -21
  90. package/dist/subcommands/handlers/per-subcommand-help-operations.d.ts +8 -0
  91. package/dist/subcommands/handlers/tools-cli-args.d.ts +57 -0
  92. package/dist/subcommands/handlers/tools-cli.d.ts +5 -0
  93. package/dist/subcommands/handlers/tools-serve.d.ts +4 -0
  94. package/dist/{subcommands-VTMIZQBP.js → subcommands-OCJNO57E.js} +2 -2
  95. package/dist/tool-gateway-http.d.ts +30 -0
  96. package/dist/tool-gateway.d.ts +185 -0
  97. package/dist/tools-cli-44MLIUQO.js +509 -0
  98. package/dist/tools-serve-TRCET2M7.js +102 -0
  99. package/dist/{tools-skills-55QVTMSK.js → tools-skills-WDTMCE46.js} +7 -2
  100. package/dist/tuneup-runtime-checks.d.ts +7 -0
  101. package/dist/tuneup-types.d.ts +118 -0
  102. package/dist/tuneup.d.ts +3 -122
  103. package/dist/{typesafe-YBEADNAH.js → typesafe-QLSMFU7J.js} +1 -1
  104. package/dist/{version-help-RBZC5GZZ.js → version-help-X6CC4CFY.js} +2 -2
  105. package/dist/{webui-server-HIUY3RHD.js → webui-server-V2R5WQUK.js} +79 -3
  106. package/dist/wiring/provider-runtime-setup.d.ts +3 -1
  107. package/dist/wiring/session-runtime.d.ts +1 -1
  108. package/dist/wrongforge-gateway-UOFBK56J.js +92 -0
  109. package/dist/wrongforge-leader/wrongforge-leader-gateway.d.ts +9 -0
  110. package/dist/wrongforge-leader/wrongforge-leader-picker.d.ts +12 -0
  111. package/dist/wrongforge-leader/wrongforge-leader-provider.d.ts +49 -0
  112. package/package.json +34 -30
  113. package/dist/auth-menu/anthropic-oauth.d.ts +0 -43
  114. package/dist/chunk-XT3NAHRX.js +0 -337
  115. package/dist/chunk-ZNNFEKDZ.js +0 -319
  116. package/dist/mcp-2XWBJEQN.js +0 -464
  117. package/dist/short-circuit-flags-CPCT5MWB.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") {
@@ -846,157 +612,395 @@ async function tokenRevoke(args, deps) {
846
612
  );
847
613
  return 0;
848
614
  }
849
- function printHelp(deps) {
850
- deps.renderer.write(`Usage: wstack hq <serve | token | audit | service>
615
+ function printTokenHelp(deps) {
616
+ deps.renderer.write(`Usage: wstack hq token <create | list | revoke>
851
617
  `);
852
618
  deps.renderer.write("\n");
853
- deps.renderer.write(` wstack hq Start the HQ command center server.
854
- `);
855
- deps.renderer.write(` wstack hq serve Same as above (explicit form).
856
- `);
857
619
  deps.renderer.write(
858
- ` wstack hq token create [label] [--ttl <dur>] Mint a browser token, enter token mode.
620
+ ` wstack hq token create [label] [--ttl <dur>] Mint a browser token (enters token mode).
859
621
  `
860
622
  );
861
623
  deps.renderer.write(
862
- ` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
624
+ ` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
863
625
  `
864
626
  );
865
- deps.renderer.write(` wstack hq token list List issued browser tokens.
866
- `);
867
- deps.renderer.write(` wstack hq token list --client List issued client tokens.
868
- `);
869
627
  deps.renderer.write(
870
- ` wstack hq token revoke <id> Revoke a browser token (id prefix match).
628
+ ` wstack hq token list List issued browser tokens (alias: ls).
871
629
  `
872
630
  );
873
- deps.renderer.write(` wstack hq token revoke --client <id> Revoke a client token.
874
- `);
875
631
  deps.renderer.write(
876
- ` wstack hq audit verify Re-derive auth.json contentHash for forensic comparison.
632
+ ` wstack hq token list --client List issued client tokens.
877
633
  `
878
634
  );
879
635
  deps.renderer.write(
880
- ` wstack hq service install Install persistent Ubuntu/systemd service + updater timer.
636
+ ` wstack hq token revoke <id> Revoke a browser token (id prefix match).
881
637
  `
882
638
  );
883
- deps.renderer.write(` wstack hq service status Show service and updater status.
884
- `);
885
- deps.renderer.write(` wstack hq service update Run the guarded update job now.
886
- `);
887
639
  deps.renderer.write(
888
- ` wstack hq service uninstall Remove units but preserve HQ data and secret file.
640
+ ` wstack hq token revoke --client <id> Revoke a client token.
889
641
  `
890
642
  );
891
643
  deps.renderer.write("\n");
892
- deps.renderer.write(`Flags (apply to all subcommands):
644
+ deps.renderer.write(`The token secret is printed once at create time and cannot be recovered
645
+ `);
646
+ deps.renderer.write(`from auth.json (only its SHA-256 verifier is persisted). Tokens live in
647
+ `);
648
+ deps.renderer.write(`<dataDir>/auth.json (default ~/.wrongstack/hq/auth.json).
649
+ `);
650
+ deps.renderer.write("\n");
651
+ deps.renderer.write(`Flags:
893
652
  `);
894
653
  deps.renderer.write(
895
654
  ` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
896
655
  `
897
656
  );
898
657
  deps.renderer.write(
899
- ` --host <ip> Bind host (default 0.0.0.0; use 127.0.0.1 for local-only).
658
+ ` --client, -c Operate on client tokens instead of browser tokens.
900
659
  `
901
660
  );
902
661
  deps.renderer.write(
903
- ` --tunnel Publish a temporary *.trycloudflare.com URL (requires cloudflared).
662
+ ` --capabilities <csv> Comma-separated capability grants (browser: control.enqueue,control.approve; client: telemetry.publish,control.execute).
904
663
  `
905
664
  );
906
665
  deps.renderer.write(
907
- ` --hq-public-url <https-origin> Trust a persistent TLS reverse-proxy origin; binds loopback and requires password mode.
666
+ ` --ttl <duration> Stamp an expiresAt on the token (e.g. --ttl 1h, --ttl 7d, --ttl 3600s).
908
667
  `
909
668
  );
910
- deps.renderer.write(` --password <value> Set or rotate the browser login password.
669
+ deps.renderer.write("\n");
670
+ deps.renderer.write(`Run \`wstack hq --help\` for the full HQ command list.
911
671
  `);
672
+ }
673
+
674
+ // src/subcommands/handlers/hq.ts
675
+ var hqCmd = async (args, deps) => {
676
+ const sub = args[0];
677
+ if (deps.flags?.["help"] === true && (!sub || sub === "serve")) {
678
+ printHelp(deps);
679
+ return 0;
680
+ }
681
+ if (!sub || sub === "serve") {
682
+ return startServer(deps);
683
+ }
684
+ if (sub === "token") {
685
+ return hqTokenCmd(args.slice(1), deps);
686
+ }
687
+ if (sub === "audit") {
688
+ return hqAuditCmd(args.slice(1), deps);
689
+ }
690
+ if (sub === "alerts") {
691
+ return hqAlertsCmd(args.slice(1), deps);
692
+ }
693
+ if (sub === "service") {
694
+ const { hqServiceCmd } = await import("./hq-service-TQNPIRHL.js");
695
+ return hqServiceCmd(args.slice(1), deps);
696
+ }
697
+ if (sub === "help" || sub === "--help") {
698
+ printHelp(deps);
699
+ return 0;
700
+ }
701
+ deps.renderer.writeError(`Unknown hq subcommand: ${sub}
702
+ `);
703
+ printHelp(deps);
704
+ return 1;
705
+ };
706
+ async function startServer(deps) {
707
+ const { startHqServer } = await import("./hq-server-YK7FBLL6.js");
708
+ const dataDir = resolveDataDir(deps);
709
+ const flags = deps.flags ?? {};
710
+ const rawPublicUrl = typeof flags["hq-public-url"] === "string" ? flags["hq-public-url"] : process.env.WRONGSTACK_HQ_PUBLIC_URL;
711
+ let publicOrigin;
712
+ if (rawPublicUrl !== void 0 && rawPublicUrl.trim() !== "") {
713
+ try {
714
+ publicOrigin = normalizeHqPublicOrigin(rawPublicUrl);
715
+ } catch (cause) {
716
+ deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
717
+ `);
718
+ return 1;
719
+ }
720
+ }
721
+ const host = typeof flags["host"] === "string" ? flags["host"] : publicOrigin !== void 0 ? "127.0.0.1" : HQ_CLI_DEFAULT_HOST;
722
+ const rawPort = typeof flags["port"] === "string" ? flags["port"].trim() : void 0;
723
+ const port = rawPort === void 0 ? 3499 : /^\d+$/.test(rawPort) ? Number(rawPort) : Number.NaN;
724
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
725
+ deps.renderer.writeError(`Invalid --port value: ${String(flags["port"])}
726
+ `);
727
+ return 1;
728
+ }
729
+ if (publicOrigin !== void 0 && !isLoopbackHost(host)) {
730
+ deps.renderer.writeError(
731
+ "Persistent public HQ requires a loopback bind. Remove --host or use --host 127.0.0.1.\n"
732
+ );
733
+ return 1;
734
+ }
735
+ const strictPort = flags["strict-port"] === true;
736
+ const open = flags["open"] === true;
737
+ let password;
738
+ try {
739
+ password = await resolveHqPasswordInput({
740
+ ...typeof flags["password"] === "string" ? { explicitPassword: flags["password"] } : {}
741
+ });
742
+ } catch (cause) {
743
+ deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
744
+ `);
745
+ return 1;
746
+ }
747
+ const bootstrapPasswordOnly = /^(?:1|true)$/i.test(
748
+ process.env.WRONGSTACK_HQ_BOOTSTRAP_PASSWORD_ONLY?.trim() ?? ""
749
+ );
750
+ const allowInsecureOpen = flags["insecure-open"] === true;
751
+ const rawAllowlist = typeof flags["hq-allowlist"] === "string" ? flags["hq-allowlist"] : process.env.WRONGSTACK_HQ_ALLOWLIST;
752
+ let ipAllowlist;
753
+ try {
754
+ ipAllowlist = parseHqIpAllowlist(rawAllowlist);
755
+ } catch (cause) {
756
+ deps.renderer.writeError(`${cause instanceof Error ? cause.message : String(cause)}
757
+ `);
758
+ return 1;
759
+ }
760
+ const rawTtl = typeof flags["hq-token-ttl"] === "string" ? flags["hq-token-ttl"] : void 0;
761
+ let tokenTtlMs;
762
+ if (rawTtl !== void 0 && rawTtl.length > 0) {
763
+ const parsed = parseTokenTtlValue(rawTtl);
764
+ if (parsed.error) {
765
+ deps.renderer.writeError(`Invalid --hq-token-ttl: ${parsed.error}
766
+ `);
767
+ return 1;
768
+ }
769
+ tokenTtlMs = parsed.value;
770
+ }
771
+ const rawHops = typeof flags["hq-trusted-proxy-hops"] === "string" ? flags["hq-trusted-proxy-hops"] : void 0;
772
+ let trustedProxyHops;
773
+ if (rawHops !== void 0 && rawHops.length > 0) {
774
+ const parsed = Number.parseInt(rawHops, 10);
775
+ if (!Number.isFinite(parsed) || parsed < 0 || String(parsed) !== rawHops.trim()) {
776
+ deps.renderer.writeError(
777
+ "--hq-trusted-proxy-hops must be a non-negative integer (0 = ignore X-Forwarded-For).\n"
778
+ );
779
+ return 1;
780
+ }
781
+ trustedProxyHops = parsed;
782
+ }
783
+ if (password !== void 0 && password.length < 8) {
784
+ deps.renderer.writeError("HQ password must be at least 8 characters.\n");
785
+ return 1;
786
+ }
787
+ let handle;
788
+ try {
789
+ handle = await startHqServer({
790
+ host,
791
+ port,
792
+ strictPort,
793
+ dataDir,
794
+ allowInsecureOpen,
795
+ secureCookies: publicOrigin !== void 0,
796
+ requireBrowserAuth: publicOrigin !== void 0,
797
+ ...password !== void 0 ? { password } : {},
798
+ ...bootstrapPasswordOnly ? { bootstrapPasswordOnly: true } : {},
799
+ ...tokenTtlMs !== void 0 ? { tokenTtlMs } : {},
800
+ ...trustedProxyHops !== void 0 ? { trustedProxyHops } : {},
801
+ ...ipAllowlist !== void 0 ? { ipAllowlist } : {}
802
+ });
803
+ } catch (err) {
804
+ if (err instanceof HqInsecureExposureError) {
805
+ deps.renderer.writeError(`${err.message}
806
+ `);
807
+ return 1;
808
+ }
809
+ throw err;
810
+ }
811
+ if (publicOrigin !== void 0) {
812
+ if (handle.firstRunSetup?.passwordMode !== true) {
813
+ await handle.close();
814
+ deps.renderer.writeError(
815
+ "Persistent public HQ requires password mode. Set WRONGSTACK_HQ_PASSWORD and retry.\n"
816
+ );
817
+ return 1;
818
+ }
819
+ handle.trustPublicOrigin(publicOrigin);
820
+ deps.renderer.write(`Public endpoint: ${publicOrigin}/
821
+ `);
822
+ deps.renderer.write(`Public mobile: ${publicOrigin}/mobile
823
+ `);
824
+ }
825
+ if (open) {
826
+ try {
827
+ const { openBrowser } = await import("@wrongstack/webui-server");
828
+ openBrowser(
829
+ publicOrigin ?? handle.firstRunSetup?.browserUrl ?? `http://${handle.host}:${handle.port}`
830
+ );
831
+ } catch {
832
+ }
833
+ }
834
+ writeStartupInfo(deps, handle);
912
835
  deps.renderer.write(
913
- ` --insecure-open Allow a non-loopback bind with no token/password set.
836
+ ipAllowlist === void 0 ? "Network allowlist: disabled (authentication remains required when configured).\n" : `Network allowlist: active (${ipAllowlist.length} configured rule${ipAllowlist.length === 1 ? "" : "s"} + loopback).
914
837
  `
915
838
  );
916
- deps.renderer.write(` --port <n> Bind port (default 3499).
839
+ await new Promise((resolve2) => {
840
+ const shutdown = async () => {
841
+ try {
842
+ await handle.close();
843
+ } catch (err) {
844
+ deps.renderer.write(`HQ server close error: ${String(err)}
917
845
  `);
918
- deps.renderer.write(` --strict-port Fail if port is in use.
846
+ }
847
+ resolve2();
848
+ };
849
+ process.on("SIGINT", shutdown);
850
+ process.on("SIGTERM", shutdown);
851
+ });
852
+ return 0;
853
+ }
854
+ function writeStartupInfo(deps, handle) {
855
+ deps.renderer.write(`WrongStack HQ listening on http://${handle.host}:${handle.port}
856
+ `);
857
+ if (!handle.firstRunSetup) {
858
+ deps.renderer.write(`Client endpoint: ws://${handle.host}:${handle.port}/ws/client
859
+ `);
860
+ deps.renderer.write(`Browser endpoint: http://${handle.host}:${handle.port}
861
+ `);
862
+ deps.renderer.write(
863
+ `Mobile endpoint: http://${handle.host}:${handle.port}/mobile (password required)
864
+ `
865
+ );
866
+ return;
867
+ }
868
+ if (process.env.WRONGSTACK_HQ_SUPPRESS_STARTUP_SECRETS === "1") {
869
+ deps.renderer.write(`Browser endpoint: http://${handle.host}:${handle.port}
870
+ `);
871
+ deps.renderer.write(
872
+ `Mobile endpoint: http://${handle.host}:${handle.port}/mobile (password required)
873
+ `
874
+ );
875
+ deps.renderer.write(`Client endpoint: ws://${handle.host}:${handle.port}/ws/client
876
+ `);
877
+ deps.renderer.write(
878
+ `Startup credentials suppressed; auth state is stored in ${handle.firstRunSetup.dataDir}.
879
+ `
880
+ );
881
+ return;
882
+ }
883
+ const mobileUrl = new URL(handle.firstRunSetup.browserUrl);
884
+ mobileUrl.pathname = "/mobile";
885
+ mobileUrl.search = "";
886
+ mobileUrl.hash = "";
887
+ deps.renderer.write(`Browser endpoint: ${handle.firstRunSetup.browserUrl}
888
+ `);
889
+ deps.renderer.write(`Mobile endpoint: ${mobileUrl.toString()} (password required)
890
+ `);
891
+ deps.renderer.write(`Client endpoint: ${handle.firstRunSetup.clientUrl}
919
892
  `);
920
893
  deps.renderer.write(
921
- ` --hq-token-ttl <dur> Stamp an expiresAt on first-run tokens (e.g. 1h, 7d, 3600s).
894
+ handle.firstRunSetup.createdAuth ? `
895
+ First-run HQ auth created in ${handle.firstRunSetup.dataDir}
896
+ ` : `
897
+ HQ auth loaded from ${handle.firstRunSetup.dataDir}
922
898
  `
923
899
  );
900
+ deps.renderer.write(`Start clients with:
901
+ `);
902
+ deps.renderer.write(` WRONGSTACK_HQ_URL=${handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_URL}
903
+ `);
904
+ if (handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_TOKEN) {
905
+ deps.renderer.write(
906
+ ` WRONGSTACK_HQ_TOKEN=${handle.firstRunSetup.clientEnv.WRONGSTACK_HQ_TOKEN}
907
+ `
908
+ );
909
+ }
910
+ }
911
+ function printHelp(deps) {
912
+ deps.renderer.write(`Usage: wstack hq <serve | token | audit | service>
913
+ `);
914
+ deps.renderer.write("\n");
915
+ deps.renderer.write(` wstack hq Start the HQ command center server.
916
+ `);
917
+ deps.renderer.write(` wstack hq serve Same as above (explicit form).
918
+ `);
924
919
  deps.renderer.write(
925
- ` --hq-trusted-proxy-hops <n> Trust the rightmost n X-Forwarded-For entries when rate-limiting logins.
926
- Default 0 (ignore the header). Set to the real hop count behind a tunnel,
927
- otherwise every user shares one backoff bucket. Never guess high.
920
+ ` wstack hq token create [label] [--ttl <dur>] Mint a browser token, enter token mode.
928
921
  `
929
922
  );
930
923
  deps.renderer.write(
931
- ` --hq-allowlist <csv> Optional source IP/CIDR allowlist (for example 203.0.113.4,10.0.0.0/8).
924
+ ` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
932
925
  `
933
926
  );
934
- deps.renderer.write(` --open Open the dashboard in the default browser.
927
+ deps.renderer.write(` wstack hq token list List issued browser tokens.
928
+ `);
929
+ deps.renderer.write(` wstack hq token list --client List issued client tokens.
935
930
  `);
936
931
  deps.renderer.write(
937
- ` --client, -c Operate on client tokens instead of browser tokens.
932
+ ` wstack hq token revoke <id> Revoke a browser token (id prefix match).
938
933
  `
939
934
  );
935
+ deps.renderer.write(` wstack hq token revoke --client <id> Revoke a client token.
936
+ `);
940
937
  deps.renderer.write(
941
- ` --capabilities <csv> Token grants (browser: control.enqueue,control.approve; client: telemetry.publish,control.execute).
938
+ ` wstack hq audit verify Re-derive auth.json contentHash for forensic comparison.
942
939
  `
943
940
  );
944
941
  deps.renderer.write(
945
- ` --ttl <duration> Stamp an expiresAt on the token (e.g. --ttl 1h, --ttl 7d, --ttl 3600s).
942
+ ` wstack hq service install Install persistent Ubuntu/systemd service + updater timer.
946
943
  `
947
944
  );
948
- deps.renderer.write("\n");
949
- deps.renderer.write(`auth.json schema version: ${HQ_AUTH_FILE_VERSION}.
945
+ deps.renderer.write(` wstack hq service status Show service and updater status.
950
946
  `);
951
- }
952
- function printTokenHelp(deps) {
953
- deps.renderer.write(`Usage: wstack hq token <create | list | revoke>
947
+ deps.renderer.write(` wstack hq service update Run the guarded update job now.
954
948
  `);
955
- deps.renderer.write("\n");
956
949
  deps.renderer.write(
957
- ` wstack hq token create [label] [--ttl <dur>] Mint a browser token (enters token mode).
950
+ ` wstack hq service uninstall Remove units but preserve HQ data and secret file.
958
951
  `
959
952
  );
953
+ deps.renderer.write("\n");
954
+ deps.renderer.write(`Flags (apply to all subcommands):
955
+ `);
960
956
  deps.renderer.write(
961
- ` wstack hq token create --client [label] [--ttl <dur>] Mint a client token (/ws/client).
957
+ ` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
962
958
  `
963
959
  );
964
960
  deps.renderer.write(
965
- ` wstack hq token list List issued browser tokens (alias: ls).
961
+ ` --host <ip> Bind host (default 0.0.0.0; use 127.0.0.1 for local-only).
966
962
  `
967
963
  );
968
964
  deps.renderer.write(
969
- ` wstack hq token list --client List issued client tokens.
965
+ ` --tunnel Publish a temporary *.trycloudflare.com URL (requires cloudflared).
970
966
  `
971
967
  );
972
968
  deps.renderer.write(
973
- ` wstack hq token revoke <id> Revoke a browser token (id prefix match).
969
+ ` --hq-public-url <https-origin> Trust a persistent TLS reverse-proxy origin; binds loopback and requires password mode.
974
970
  `
975
971
  );
972
+ deps.renderer.write(` --password <value> Set or rotate the browser login password.
973
+ `);
976
974
  deps.renderer.write(
977
- ` wstack hq token revoke --client <id> Revoke a client token.
975
+ ` --insecure-open Allow a non-loopback bind with no token/password set.
978
976
  `
979
977
  );
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).
978
+ deps.renderer.write(` --port <n> Bind port (default 3499).
986
979
  `);
987
- deps.renderer.write("\n");
988
- deps.renderer.write(`Flags:
980
+ deps.renderer.write(` --strict-port Fail if port is in use.
989
981
  `);
990
982
  deps.renderer.write(
991
- ` --data-dir <path> Override HQ data directory (default ~/.wrongstack/hq).
983
+ ` --hq-token-ttl <dur> Stamp an expiresAt on first-run tokens (e.g. 1h, 7d, 3600s).
984
+ `
985
+ );
986
+ deps.renderer.write(
987
+ ` --hq-trusted-proxy-hops <n> Trust the rightmost n X-Forwarded-For entries when rate-limiting logins.
988
+ Default 0 (ignore the header). Set to the real hop count behind a tunnel,
989
+ otherwise every user shares one backoff bucket. Never guess high.
992
990
  `
993
991
  );
992
+ deps.renderer.write(
993
+ ` --hq-allowlist <csv> Optional source IP/CIDR allowlist (for example 203.0.113.4,10.0.0.0/8).
994
+ `
995
+ );
996
+ deps.renderer.write(` --open Open the dashboard in the default browser.
997
+ `);
994
998
  deps.renderer.write(
995
999
  ` --client, -c Operate on client tokens instead of browser tokens.
996
1000
  `
997
1001
  );
998
1002
  deps.renderer.write(
999
- ` --capabilities <csv> Comma-separated capability grants (browser: control.enqueue,control.approve; client: telemetry.publish,control.execute).
1003
+ ` --capabilities <csv> Token grants (browser: control.enqueue,control.approve; client: telemetry.publish,control.execute).
1000
1004
  `
1001
1005
  );
1002
1006
  deps.renderer.write(
@@ -1004,10 +1008,10 @@ function printTokenHelp(deps) {
1004
1008
  `
1005
1009
  );
1006
1010
  deps.renderer.write("\n");
1007
- deps.renderer.write(`Run \`wstack hq --help\` for the full HQ command list.
1011
+ deps.renderer.write(`auth.json schema version: ${HQ_AUTH_FILE_VERSION}.
1008
1012
  `);
1009
1013
  }
1010
1014
  export {
1011
1015
  hqCmd
1012
1016
  };
1013
- //# sourceMappingURL=hq-JD5AZVMG.js.map
1017
+ //# sourceMappingURL=hq-54PQYR5O.js.map