@wrongstack/cli 1.0.1 → 1.0.4

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 (109) hide show
  1. package/data/providers.json +21 -21
  2. package/dist/{acp-3LARESEO.js → acp-AB54FWM2.js} +122 -39
  3. package/dist/acp-agent-list.d.ts +14 -0
  4. package/dist/acp-hq-telemetry.d.ts +9 -2
  5. package/dist/acp-mcp-servers.d.ts +49 -0
  6. package/dist/acp-server-agent.d.ts +18 -6
  7. package/dist/{auth-ZUMCDNE5.js → auth-WDMSNZ3S.js} +79 -55
  8. package/dist/auth-menu/index.d.ts +1 -0
  9. package/dist/auth-menu/oauth-menu.d.ts +3 -1
  10. package/dist/auth-menu/panel-service.d.ts +2 -0
  11. package/dist/auth-menu/provider-auth-login.d.ts +12 -0
  12. package/dist/auth-menu/types.d.ts +3 -0
  13. package/dist/auto-discover-providers-DXR25GTG.js +7 -0
  14. package/dist/boot/dispatch-webui.d.ts +1 -0
  15. package/dist/boot/short-circuit-hq.d.ts +2 -0
  16. package/dist/boot/system-prompt-builder.d.ts +7 -0
  17. package/dist/boot/system-prompt.d.ts +2 -1
  18. package/dist/boot/tool-registry.d.ts +18 -1
  19. package/dist/chimera-reviewer-policy.d.ts +1 -1
  20. package/dist/{chunk-C2E24IXZ.js → chunk-3I5ZJXM5.js} +147 -241
  21. package/dist/{chunk-JHPXJRRI.js → chunk-4XCBEUBX.js} +13 -3
  22. package/dist/{chunk-FPIWTJ2G.js → chunk-6WRKAACA.js} +4 -4
  23. package/dist/chunk-AUQDGU5D.js +32 -0
  24. package/dist/{chunk-D2VCUN7M.js → chunk-BIWE22O5.js} +17 -17
  25. package/dist/{chunk-VOTTENR5.js → chunk-CDY4D5QK.js} +2 -2
  26. package/dist/chunk-D5GB5MHA.js +87 -0
  27. package/dist/chunk-DAKVBMD4.js +36 -0
  28. package/dist/{chunk-E6RFXHWD.js → chunk-EK2P53GL.js} +3 -2
  29. package/dist/chunk-I7V2OYVH.js +242 -0
  30. package/dist/{chunk-MXEJEYT7.js → chunk-JX6KL7OF.js} +15 -5
  31. package/dist/{chunk-ZSZ3EYWH.js → chunk-KEPABQYT.js} +109 -6
  32. package/dist/{chunk-IMWXXF4J.js → chunk-KNMIBKLV.js} +79 -1
  33. package/dist/{chunk-MNQQ4ZNF.js → chunk-ONOT5OJ2.js} +290 -687
  34. package/dist/chunk-Q7E3BPDU.js +62 -0
  35. package/dist/chunk-SIGUWXAA.js +43 -0
  36. package/dist/{chunk-TEBMCYZN.js → chunk-SIS37L3W.js} +58 -2
  37. package/dist/{chunk-KHWDTNBZ.js → chunk-U6PGWDMZ.js} +5 -5
  38. package/dist/{chunk-5IA5337I.js → chunk-XJ7K67PP.js} +8 -2
  39. package/dist/{cli-context-7X3M6U73.js → cli-context-WFC3BNGM.js} +131 -25
  40. package/dist/{cli-main-53KG6HAV.js → cli-main-GU3YZLMO.js} +3986 -2827
  41. package/dist/{diag-doctor-7LYUYU35.js → diag-doctor-PCER6KHH.js} +2 -2
  42. package/dist/execute-deps.d.ts +1 -0
  43. package/dist/{execution-MIBWBYB6.js → execution-RAZDDQFC.js} +115 -16
  44. package/dist/execution-specialist-trigger.d.ts +28 -0
  45. package/dist/fleet/acp-live-progress.d.ts +19 -0
  46. package/dist/fleet/host-acp-runner-cache.d.ts +3 -1
  47. package/dist/fleet/host-acp.d.ts +14 -2
  48. package/dist/fleet/host.d.ts +1 -0
  49. package/dist/{hq-IG3YRGCO.js → hq-5XHEIIH6.js} +127 -8
  50. package/dist/hq-command-controller.d.ts +31 -2
  51. package/dist/hq-server/client-address.d.ts +2 -0
  52. package/dist/hq-server/ip-allowlist.d.ts +16 -0
  53. package/dist/hq-server/preflight.d.ts +1 -0
  54. package/dist/hq-server/routes/auth/password-routes.d.ts +1 -1
  55. package/dist/hq-server/routes.d.ts +5 -4
  56. package/dist/hq-server/secret-input.d.ts +7 -0
  57. package/dist/hq-server/types.d.ts +3 -2
  58. package/dist/hq-server/upgrade-handler.d.ts +2 -0
  59. package/dist/hq-server/utils.d.ts +6 -0
  60. package/dist/{hq-server-OILU3NPI.js → hq-server-CNUTBWTK.js} +4 -3
  61. package/dist/hq-server.d.ts +4 -0
  62. package/dist/hq-service-L5INQFM4.js +507 -0
  63. package/dist/hq-service.d.ts +22 -0
  64. package/dist/index.js +4 -4
  65. package/dist/ip-allowlist-NHXO6AKJ.js +9 -0
  66. package/dist/live-settings-input.d.ts +4 -0
  67. package/dist/{mailbox-serve-VUCWZ2SJ.js → mailbox-serve-X3CR6VFW.js} +4 -3
  68. package/dist/{modeldiag-FMJT37SN.js → modeldiag-46CMWUNM.js} +31 -24
  69. package/dist/{per-subcommand-help-BWBHYVJO.js → per-subcommand-help-DHTYU524.js} +2 -2
  70. package/dist/{plugin-usage-HFBI3TFT.js → plugin-usage-M6KET4OZ.js} +2 -2
  71. package/dist/{plugins-PZ2VZL7V.js → plugins-WJLW6ULV.js} +3 -3
  72. package/dist/projects-LMNAGGEU.js +36 -0
  73. package/dist/{providers-models-YVMVYWRS.js → providers-models-6MOGAOVK.js} +2 -2
  74. package/dist/{proxy-wiring-MGRSKZZB.js → proxy-wiring-K2DORFFL.js} +3 -3
  75. package/dist/short-circuit-flags-YDMLAQ5R.js +10 -0
  76. package/dist/slash-commands/acp.d.ts +3 -0
  77. package/dist/slash-commands/codebase-map.d.ts +11 -0
  78. package/dist/slash-commands/command-context.d.ts +29 -0
  79. package/dist/slash-commands/context.d.ts +10 -0
  80. package/dist/slash-commands/ensemble.d.ts +1 -1
  81. package/dist/slash-commands/openai-quota.d.ts +31 -0
  82. package/dist/slash-commands/profile.d.ts +7 -0
  83. package/dist/subcommands/handlers/hq.d.ts +1 -0
  84. package/dist/subcommands/handlers/projects.d.ts +14 -0
  85. package/dist/{subcommands-WJLBHVKC.js → subcommands-7N4RIK6H.js} +2 -2
  86. package/dist/{version-help-MYKF724O.js → version-help-WXI42PZ3.js} +2 -2
  87. package/dist/{webui-server-5R35BHWZ.js → webui-server-OBV5JYJV.js} +27 -4
  88. package/dist/webui-server-options.d.ts +1 -0
  89. package/dist/wiring/atlas-prompt-contributor.d.ts +54 -0
  90. package/dist/wiring/cli-execute-builder.d.ts +1 -0
  91. package/dist/wiring/cli-slash-commands-setup.d.ts +7 -0
  92. package/dist/wiring/codebase-embeddings.d.ts +31 -0
  93. package/dist/wiring/command-host-state.d.ts +9 -0
  94. package/dist/wiring/concept-summarizer.d.ts +32 -0
  95. package/dist/wiring/external-plugins.d.ts +1 -0
  96. package/dist/wiring/lifecycle-plugins.d.ts +2 -1
  97. package/dist/wiring/pipeline.d.ts +2 -0
  98. package/dist/wiring/plugins.d.ts +2 -1
  99. package/dist/wiring/provider-runtime-setup.d.ts +1 -1
  100. package/dist/wiring/provider.d.ts +2 -1
  101. package/dist/wiring/session-command-handlers.d.ts +2 -1
  102. package/dist/wiring/slash-commands.d.ts +1 -0
  103. package/package.json +29 -29
  104. package/dist/auto-discover-providers-VQU3IZMR.js +0 -7
  105. package/dist/chunk-52C7PD2H.js +0 -61
  106. package/dist/chunk-Q5GTM25S.js +0 -17
  107. package/dist/chunk-TNK6237M.js +0 -262
  108. package/dist/projects-L6C6XLDY.js +0 -37
  109. package/dist/short-circuit-flags-UREFSYBL.js +0 -10
@@ -129,11 +129,21 @@ function scheduleImmediateProbe(runner) {
129
129
  void runner.poke();
130
130
  }
131
131
  function buildHealthUrl(rawBase) {
132
- const trimmed = rawBase.trim().replace(/\/+$/, "");
133
- return `${trimmed}${HEALTH_PATH}`;
132
+ const trimmed = rawBase.trim();
133
+ try {
134
+ const base = new URL(trimmed);
135
+ base.search = "";
136
+ base.hash = "";
137
+ base.pathname = `${base.pathname.replace(/\/+$/, "")}${HEALTH_PATH}`;
138
+ return base.toString();
139
+ } catch {
140
+ const boundary = trimmed.search(/[?#]/);
141
+ const pathBase = boundary === -1 ? trimmed : trimmed.slice(0, boundary);
142
+ return `${pathBase.replace(/\/+$/, "")}${HEALTH_PATH}`;
143
+ }
134
144
  }
135
145
 
136
146
  export {
137
147
  startProxyProbe
138
148
  };
139
- //# sourceMappingURL=chunk-JHPXJRRI.js.map
149
+ //# sourceMappingURL=chunk-4XCBEUBX.js.map
@@ -67,12 +67,12 @@ var helpCmd = async (_args, deps) => {
67
67
  color.bold("Common flags"),
68
68
  " --yolo / --no-yolo Force auto-approval on or off at startup",
69
69
  " --confirm-destructive Deprecated \u2014 YOLO no longer prompts by destructiveness",
70
- " --yolo-destructive Deprecated compatibility flag; YOLO no longer prompts by destructiveness",
70
+ " --yolo-destructive Let YOLO run every destructive kind it is allowed to (see /yolo confirm)",
71
71
  " --tui / --no-tui Force or disable TUI mode",
72
72
  " --mouse Full mouse mode in the TUI (in-app scroll + clickable UI)",
73
73
  " --desktop Open WrongStack Desktop (requires @wrongstack/desktop)",
74
- " --hq [--host <h>] [--port <n>] [--password <secret>] [--tunnel] [--open]",
75
- " Start HQ; --tunnel publishes a temporary HTTPS URL",
74
+ " --hq [--host <h>] [--port <n>] [--password <secret>] [--hq-allowlist <ip,cidr,...>] [--tunnel] [--hq-public-url <https-origin>] [--open]",
75
+ " Start HQ; publish through a temporary tunnel or persistent TLS proxy",
76
76
  " --webui [--host <h>] [--port <n>] [--webui-token <t>] [--open]",
77
77
  " [--webui-public-url <url>] [--webui-public-ws-url <url>] [--webui-require-token]",
78
78
  " Serve the browser UI + WS bridge (prints a token URL,",
@@ -93,4 +93,4 @@ export {
93
93
  versionCmd,
94
94
  helpCmd
95
95
  };
96
- //# sourceMappingURL=chunk-FPIWTJ2G.js.map
96
+ //# sourceMappingURL=chunk-6WRKAACA.js.map
@@ -0,0 +1,32 @@
1
+
2
+ // src/hq-server/secret-input.ts
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ var MAX_PASSWORD_FILE_BYTES = 4096;
6
+ async function resolveHqPasswordInput(options = {}) {
7
+ const environment = options.environment ?? process.env;
8
+ const direct = options.explicitPassword ?? environment.WRONGSTACK_HQ_PASSWORD;
9
+ const file = environment.WRONGSTACK_HQ_PASSWORD_FILE;
10
+ if (direct !== void 0 && file !== void 0) {
11
+ throw new Error(
12
+ "Set only one of --password/WRONGSTACK_HQ_PASSWORD or WRONGSTACK_HQ_PASSWORD_FILE."
13
+ );
14
+ }
15
+ if (file === void 0) return direct;
16
+ if (!path.isAbsolute(file)) {
17
+ throw new Error("WRONGSTACK_HQ_PASSWORD_FILE must be an absolute path.");
18
+ }
19
+ const stat2 = await fs.stat(file);
20
+ if (!stat2.isFile()) throw new Error("WRONGSTACK_HQ_PASSWORD_FILE must point to a regular file.");
21
+ if (stat2.size > MAX_PASSWORD_FILE_BYTES) {
22
+ throw new Error(`WRONGSTACK_HQ_PASSWORD_FILE exceeds ${MAX_PASSWORD_FILE_BYTES} bytes.`);
23
+ }
24
+ const raw = await fs.readFile(file, "utf8");
25
+ if (raw.includes("\0")) throw new Error("WRONGSTACK_HQ_PASSWORD_FILE contains a NUL byte.");
26
+ return raw.replace(/\r?\n$/, "");
27
+ }
28
+
29
+ export {
30
+ resolveHqPasswordInput
31
+ };
32
+ //# sourceMappingURL=chunk-AUQDGU5D.js.map
@@ -1,9 +1,9 @@
1
1
 
2
2
  // src/subcommands/index.ts
3
3
  var loaders = {
4
- acp: async () => (await import("./acp-3LARESEO.js")).acpCmd,
4
+ acp: async () => (await import("./acp-AB54FWM2.js")).acpCmd,
5
5
  init: async () => (await import("./init-DRMMFG2G.js")).initCmd,
6
- auth: async () => (await import("./auth-ZUMCDNE5.js")).authCmd,
6
+ auth: async () => (await import("./auth-WDMSNZ3S.js")).authCmd,
7
7
  update: async () => (await import("./update-5DPB4YD4.js")).updateCmd,
8
8
  sessions: async () => (await import("./sessions-config-UL45XKX7.js")).sessionsCmd,
9
9
  config: async () => (await import("./sessions-config-UL45XKX7.js")).configCmd,
@@ -14,25 +14,25 @@ var loaders = {
14
14
  audit: async () => (await import("./audit-46YK5DSC.js")).auditCmd,
15
15
  tools: async () => (await import("./tools-skills-GH24UCBY.js")).toolsCmd,
16
16
  skills: async () => (await import("./tools-skills-GH24UCBY.js")).skillsCmd,
17
- providers: async () => (await import("./providers-models-YVMVYWRS.js")).providersCmd,
18
- models: async () => (await import("./providers-models-YVMVYWRS.js")).modelsCmd,
17
+ providers: async () => (await import("./providers-models-6MOGAOVK.js")).providersCmd,
18
+ models: async () => (await import("./providers-models-6MOGAOVK.js")).modelsCmd,
19
19
  mcp: async () => (await import("./mcp-HOMLC4WE.js")).mcpCmd,
20
- plugin: async () => (await import("./plugin-usage-HFBI3TFT.js")).pluginCmd,
21
- plugins: async () => (await import("./plugin-usage-HFBI3TFT.js")).pluginCmd,
22
- diag: async () => (await import("./diag-doctor-7LYUYU35.js")).diagCmd,
23
- doctor: async () => (await import("./diag-doctor-7LYUYU35.js")).doctorCmd,
24
- "proxy-status": async () => (await import("./diag-doctor-7LYUYU35.js")).proxyCmd,
20
+ plugin: async () => (await import("./plugin-usage-M6KET4OZ.js")).pluginCmd,
21
+ plugins: async () => (await import("./plugin-usage-M6KET4OZ.js")).pluginCmd,
22
+ diag: async () => (await import("./diag-doctor-PCER6KHH.js")).diagCmd,
23
+ doctor: async () => (await import("./diag-doctor-PCER6KHH.js")).doctorCmd,
24
+ "proxy-status": async () => (await import("./diag-doctor-PCER6KHH.js")).proxyCmd,
25
25
  export: async () => (await import("./export-VVRIOWFV.js")).exportCmd,
26
- usage: async () => (await import("./plugin-usage-HFBI3TFT.js")).usageCmd,
27
- version: async () => (await import("./version-help-MYKF724O.js")).versionCmd,
28
- help: async () => (await import("./version-help-MYKF724O.js")).helpCmd,
29
- projects: async () => (await import("./projects-L6C6XLDY.js")).projectsCmd,
30
- modeldiag: async () => (await import("./modeldiag-FMJT37SN.js")).modeldiagCmd,
26
+ usage: async () => (await import("./plugin-usage-M6KET4OZ.js")).usageCmd,
27
+ version: async () => (await import("./version-help-WXI42PZ3.js")).versionCmd,
28
+ help: async () => (await import("./version-help-WXI42PZ3.js")).helpCmd,
29
+ projects: async () => (await import("./projects-LMNAGGEU.js")).projectsCmd,
30
+ modeldiag: async () => (await import("./modeldiag-46CMWUNM.js")).modeldiagCmd,
31
31
  quick: async () => (await import("./quick-CNB4Z3HS.js")).quickCmd,
32
32
  bench: async () => (await import("./bench-YBU6BVQP.js")).benchCmd,
33
33
  chronicle: async () => (await import("./chronicle-UTFETHP6.js")).chronicleCmd,
34
- hq: async () => (await import("./hq-IG3YRGCO.js")).hqCmd,
35
- mailbox: async () => (await import("./mailbox-serve-VUCWZ2SJ.js")).mailboxServeCmd,
34
+ hq: async () => (await import("./hq-5XHEIIH6.js")).hqCmd,
35
+ mailbox: async () => (await import("./mailbox-serve-X3CR6VFW.js")).mailboxServeCmd,
36
36
  permissions: async () => (await import("./permissions-CFDLXETM.js")).permissionsCmd,
37
37
  project: async () => (await import("./project-AOQI5XTK.js")).projectCmd,
38
38
  governance: async () => (await import("./governance-RYSHV675.js")).governanceCmd
@@ -51,4 +51,4 @@ export {
51
51
  subcommands,
52
52
  subcommandNames
53
53
  };
54
- //# sourceMappingURL=chunk-D2VCUN7M.js.map
54
+ //# sourceMappingURL=chunk-BIWE22O5.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  startProxyProbe
3
- } from "./chunk-JHPXJRRI.js";
3
+ } from "./chunk-4XCBEUBX.js";
4
4
 
5
5
  // src/wiring/proxy-wiring.ts
6
6
  import { applyProxyConfig } from "@wrongstack/core/wiring/proxy-rewrite";
@@ -53,4 +53,4 @@ export {
53
53
  bootstrapWrongProxy,
54
54
  shutdownWrongProxy
55
55
  };
56
- //# sourceMappingURL=chunk-VOTTENR5.js.map
56
+ //# sourceMappingURL=chunk-CDY4D5QK.js.map
@@ -0,0 +1,87 @@
1
+
2
+ // src/boot/auto-discover-providers.ts
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import { discoverOpenAICompatibleModels, resolveDiscoveryTargets } from "@wrongstack/providers";
6
+ async function readCache(file) {
7
+ try {
8
+ return JSON.parse(await fs.readFile(file, "utf8"));
9
+ } catch {
10
+ return {};
11
+ }
12
+ }
13
+ async function discoverAndMergeProviders(opts) {
14
+ const targets = resolveDiscoveryTargets(opts.config);
15
+ if (targets.length === 0) return;
16
+ const cacheFile = path.join(opts.cacheDir, "discovered-models-cache.json");
17
+ const cache = await readCache(cacheFile);
18
+ let cacheDirty = false;
19
+ await Promise.all(
20
+ targets.map(
21
+ async ({
22
+ id,
23
+ cfg,
24
+ baseUrl,
25
+ apiKey,
26
+ cacheKey,
27
+ modelDiscoveryPath,
28
+ modelDiscoveryAuthoritative
29
+ }) => {
30
+ const provider = await discoverOpenAICompatibleModels(id, {
31
+ baseUrl,
32
+ apiKey,
33
+ headers: cfg.headers,
34
+ providerName: id,
35
+ modelDiscoveryPath,
36
+ fetchImpl: opts.fetchImpl
37
+ });
38
+ if (provider) {
39
+ const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
40
+ cache[cacheKey] = { fetchedAt, provider };
41
+ cacheDirty = true;
42
+ if (modelDiscoveryAuthoritative) {
43
+ opts.registry.mergeOverlay(
44
+ { [id]: provider },
45
+ { observedAt: fetchedAt, authoritativeProviderIds: [id] }
46
+ );
47
+ } else {
48
+ opts.registry.mergeOverlay({ [id]: provider });
49
+ }
50
+ opts.logger?.info(
51
+ `auto-discovered ${Object.keys(provider.models).length} models for "${id}" from ${baseUrl}`
52
+ );
53
+ return;
54
+ }
55
+ const cached = cache[cacheKey];
56
+ if (cached) {
57
+ if (modelDiscoveryAuthoritative) {
58
+ opts.registry.mergeOverlay(
59
+ { [id]: cached.provider },
60
+ { observedAt: cached.fetchedAt, authoritativeProviderIds: [id] }
61
+ );
62
+ } else {
63
+ opts.registry.mergeOverlay({ [id]: cached.provider });
64
+ }
65
+ opts.logger?.warn(
66
+ `auto-discovery for "${id}" failed; using ${Object.keys(cached.provider.models).length} cached models from ${cached.fetchedAt}`
67
+ );
68
+ } else {
69
+ opts.logger?.warn(
70
+ `auto-discovery for "${id}" failed and no cache available (server at ${baseUrl} unreachable?)`
71
+ );
72
+ }
73
+ }
74
+ )
75
+ );
76
+ if (cacheDirty) {
77
+ try {
78
+ await fs.writeFile(cacheFile, JSON.stringify(cache), "utf8");
79
+ } catch {
80
+ }
81
+ }
82
+ }
83
+
84
+ export {
85
+ discoverAndMergeProviders
86
+ };
87
+ //# sourceMappingURL=chunk-D5GB5MHA.js.map
@@ -0,0 +1,36 @@
1
+
2
+ // src/auth-menu/loopback-server.ts
3
+ import { spawn } from "node:child_process";
4
+ import {
5
+ startLoopbackServer as startSharedLoopbackServer
6
+ } from "@wrongstack/providers/oauth";
7
+ import { callbackHtml } from "@wrongstack/providers/oauth";
8
+ var URL_METACHAR_REGEX = /[&|;<>(){}^"'`%\n\r\0]/;
9
+ function isSafeBrowserUrl(url) {
10
+ if (URL_METACHAR_REGEX.test(url)) return false;
11
+ try {
12
+ const parsed = new URL(url);
13
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
14
+ } catch {
15
+ return false;
16
+ }
17
+ }
18
+ function openBrowser(url) {
19
+ if (!isSafeBrowserUrl(url)) {
20
+ return;
21
+ }
22
+ try {
23
+ const platform = process.platform;
24
+ const { command, args } = platform === "win32" ? { command: "cmd", args: ["/c", "start", "", url] } : platform === "darwin" ? { command: "open", args: [url] } : { command: "xdg-open", args: [url] };
25
+ const child = spawn(command, args, { stdio: "ignore", windowsHide: true });
26
+ child.on("error", () => {
27
+ });
28
+ child.unref();
29
+ } catch {
30
+ }
31
+ }
32
+
33
+ export {
34
+ openBrowser
35
+ };
36
+ //# sourceMappingURL=chunk-DAKVBMD4.js.map
@@ -12,7 +12,6 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
12
12
  "version",
13
13
  "yes",
14
14
  "no-banner",
15
- "no-features",
16
15
  "tui",
17
16
  "no-tui",
18
17
  // Recovery switches. `--recover` reopens the most recent session with no
@@ -64,6 +63,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
64
63
  "pnpm",
65
64
  "yarn",
66
65
  "bun",
66
+ // `wstack hq service install` toggles.
67
+ "no-auto-update",
67
68
  // `wstack acp` booleans. Without these, parseArgs treats the value after
68
69
  // `--fs`/`--echo` as the flag's value and eats a following positional
69
70
  // (e.g. the agent list in `acp bench --fs gemini-cli,codex-cli`).
@@ -237,4 +238,4 @@ export {
237
238
  parseAuthFlags,
238
239
  parseSpawnFlags
239
240
  };
240
- //# sourceMappingURL=chunk-E6RFXHWD.js.map
241
+ //# sourceMappingURL=chunk-EK2P53GL.js.map
@@ -0,0 +1,242 @@
1
+
2
+ // src/hq-server/audit-actor.ts
3
+ import * as os from "node:os";
4
+ function resolveAuditActor() {
5
+ const user = (process.env.USERNAME ?? process.env.USER ?? "").toString();
6
+ try {
7
+ const host = os.hostname();
8
+ return user ? `${user}@${host}` : `cli@${host}`;
9
+ } catch {
10
+ return user ? `${user}@unknown` : "cli";
11
+ }
12
+ }
13
+
14
+ // src/hq-server/utils.ts
15
+ import * as fs from "node:fs/promises";
16
+ import * as os2 from "node:os";
17
+ import * as path from "node:path";
18
+ import { resolveHqDataDir } from "@wrongstack/core/hq";
19
+ import { sanitizeApiError, scrubErrorDetail } from "@wrongstack/core/security";
20
+ var TRANSCRIPT_RING_MAX = 4e3;
21
+ var MAX_TRANSCRIPT_SESSIONS = 400;
22
+ var MAX_AGENT_RINGS = 800;
23
+ function evictOldest(map, max) {
24
+ while (map.size > max) {
25
+ const oldest = map.keys().next().value;
26
+ if (oldest === void 0) break;
27
+ map.delete(oldest);
28
+ }
29
+ }
30
+ function agentRingKey(sessionId, subId) {
31
+ return sessionId ? `${sessionId}::${subId}` : subId;
32
+ }
33
+ function agentMessageToEntry(p) {
34
+ const kind = typeof p["kind"] === "string" ? p["kind"] : "text";
35
+ const role = kind === "thinking" ? "thinking" : kind === "tool_use" || kind === "tool_result" ? "tool" : kind === "error" ? "error" : kind === "status" || kind === "system" ? "system" : "assistant";
36
+ return {
37
+ ts: typeof p["ts"] === "string" ? p["ts"] : (/* @__PURE__ */ new Date()).toISOString(),
38
+ role,
39
+ text: typeof p["content"] === "string" ? p["content"] : "",
40
+ ...typeof p["toolName"] === "string" ? { tool: p["toolName"] } : {},
41
+ ...kind === "error" ? { isError: true } : {}
42
+ };
43
+ }
44
+ async function readLocalSubagentTranscript(sessionId, subagentId) {
45
+ try {
46
+ const { getSessionRegistry } = await import("@wrongstack/core/storage");
47
+ const { resolveWstackPaths, sessionScopedPath } = await import("@wrongstack/core/utils");
48
+ const globalRoot = path.dirname(resolveHqDataDir());
49
+ const registry = getSessionRegistry(globalRoot);
50
+ const entry = await registry.get(sessionId).catch(() => null);
51
+ if (!entry) return null;
52
+ const paths = resolveWstackPaths({ projectRoot: entry.projectRoot, globalRoot });
53
+ const sessionDir = sessionScopedPath(paths.projectSessions, sessionId, "");
54
+ if (!isSafePathSegment(subagentId)) return null;
55
+ const transcriptsRoot = path.resolve(sessionDir, "subagents", "transcripts");
56
+ const file = path.resolve(transcriptsRoot, subagentId, "transcript.jsonl");
57
+ if (!file.startsWith(transcriptsRoot + path.sep)) return null;
58
+ const raw = await fs.readFile(file, "utf8").catch(() => null);
59
+ if (raw === null) return null;
60
+ const out = [];
61
+ for (const line of raw.split("\n")) {
62
+ const trimmed = line.trim();
63
+ if (trimmed.length === 0) continue;
64
+ try {
65
+ out.push(agentMessageToEntry(JSON.parse(trimmed)));
66
+ } catch {
67
+ }
68
+ }
69
+ return out;
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+ function decodePathSegment(value) {
75
+ let decoded;
76
+ try {
77
+ decoded = decodeURIComponent(value);
78
+ } catch {
79
+ return null;
80
+ }
81
+ return isSafePathSegment(decoded) ? decoded : null;
82
+ }
83
+ function decodeSessionId(value) {
84
+ let decoded;
85
+ try {
86
+ decoded = decodeURIComponent(value);
87
+ } catch {
88
+ return null;
89
+ }
90
+ return isSafeSessionId(decoded) ? decoded : null;
91
+ }
92
+ var MAX_PATH_SEGMENT_LENGTH = 256;
93
+ function isSafePathSegment(value) {
94
+ if (value.length === 0 || value.length > MAX_PATH_SEGMENT_LENGTH) return false;
95
+ if (value === "." || value === "..") return false;
96
+ if (value.includes("/") || value.includes("\\")) return false;
97
+ if (value.includes("\0")) return false;
98
+ if (value.includes(":")) return false;
99
+ return true;
100
+ }
101
+ function isSafeSessionId(value) {
102
+ if (value.length === 0 || value.length > MAX_PATH_SEGMENT_LENGTH) return false;
103
+ if (value.includes("\\") || value.includes("\0") || value.includes(":")) return false;
104
+ const parts = value.split("/");
105
+ if (parts.length > 2) return false;
106
+ return parts.every((p) => p.length > 0 && p !== "." && p !== "..");
107
+ }
108
+ function displayHost(host) {
109
+ return host === "0.0.0.0" ? "127.0.0.1" : host;
110
+ }
111
+ var RequestBodyTooLargeError = class extends Error {
112
+ };
113
+ function readRequestBody(req, maxBytes = 1e6) {
114
+ return new Promise((resolve2, reject) => {
115
+ const chunks = [];
116
+ let size = 0;
117
+ let settled = false;
118
+ req.on("data", (chunk) => {
119
+ if (settled) return;
120
+ size += chunk.length;
121
+ if (size > maxBytes) {
122
+ settled = true;
123
+ chunks.length = 0;
124
+ reject(new RequestBodyTooLargeError("request body too large"));
125
+ return;
126
+ }
127
+ chunks.push(chunk);
128
+ });
129
+ req.on("end", () => {
130
+ if (!settled) resolve2(Buffer.concat(chunks).toString("utf8"));
131
+ });
132
+ req.on("error", (error) => {
133
+ if (!settled) reject(error);
134
+ });
135
+ });
136
+ }
137
+ function writeInvalidBody(res, error) {
138
+ const tooLarge = error instanceof RequestBodyTooLargeError;
139
+ res.writeHead(tooLarge ? 413 : 400, { "Content-Type": "application/json" });
140
+ res.end(JSON.stringify({ error: tooLarge ? "request body too large" : "invalid json body" }));
141
+ }
142
+ function buildHttpUrl(host, port, token) {
143
+ const url = new URL(`http://${displayHost(host)}:${port}/`);
144
+ if (token) url.searchParams.set("token", token);
145
+ return url.toString();
146
+ }
147
+ function buildBootstrapHttpUrl(host, port, bootstrapCode) {
148
+ const url = new URL(`http://${displayHost(host)}:${port}/`);
149
+ url.hash = `bootstrap=${bootstrapCode}`;
150
+ return url.toString();
151
+ }
152
+ function buildClientWsUrl(host, port, token) {
153
+ const url = new URL(`ws://${displayHost(host)}:${port}/ws/client`);
154
+ if (token) url.searchParams.set("token", token);
155
+ return url.toString();
156
+ }
157
+ function normalizeHqPublicOrigin(value) {
158
+ const parsed = new URL(value.trim());
159
+ if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || parsed.pathname !== "" && parsed.pathname !== "/" || parsed.search !== "" || parsed.hash !== "") {
160
+ throw new TypeError(
161
+ "HQ public URL must be an exact HTTPS origin without a path or credentials."
162
+ );
163
+ }
164
+ return parsed.origin;
165
+ }
166
+ function hqRuntimeMarkerPath(dataDir) {
167
+ return path.join(dataDir, "runtime.json");
168
+ }
169
+ function lanIPv4Addresses() {
170
+ const out = [];
171
+ try {
172
+ const ifaces = os2.networkInterfaces();
173
+ for (const name of Object.keys(ifaces)) {
174
+ for (const ni of ifaces[name] ?? []) {
175
+ if (ni.family === "IPv4" && !ni.internal) out.push(ni.address);
176
+ }
177
+ }
178
+ } catch {
179
+ }
180
+ return out;
181
+ }
182
+ function truncateHqSummary(value, maxLength) {
183
+ if (typeof value !== "string" || value.length === 0) return void 0;
184
+ if (value.length <= maxLength) return value;
185
+ const suffix = `\u2026[truncated:${value.length - maxLength}]`;
186
+ if (maxLength <= suffix.length) return "\u2026";
187
+ const prefixLen = maxLength - suffix.length;
188
+ return `${value.slice(0, prefixLen)}${suffix}`;
189
+ }
190
+ function hqMachineKey(hostname2, machineId) {
191
+ const hn = hostname2?.trim();
192
+ return hn ? `host:${hn.toLowerCase()}` : `mid:${machineId || "local"}`;
193
+ }
194
+ function recordTimeseriesSignal(persistence, event) {
195
+ try {
196
+ if (event.type === "session.usage") {
197
+ const p = event.payload;
198
+ persistence.timeseries.record({
199
+ ts: Date.parse(event.timestamp) || Date.now(),
200
+ ...typeof p.costUsd === "number" ? { costUsd: p.costUsd } : {},
201
+ ...typeof p.inputTokens === "number" ? { inputTokens: p.inputTokens } : {},
202
+ ...typeof p.outputTokens === "number" ? { outputTokens: p.outputTokens } : {},
203
+ ...typeof p.model === "string" && p.model.length > 0 ? { model: p.model } : {},
204
+ ...typeof p.provider === "string" && p.provider.length > 0 ? { provider: p.provider } : {},
205
+ ...typeof p.cacheRead === "number" ? { cacheRead: p.cacheRead } : {},
206
+ ...typeof p.cacheWrite === "number" ? { cacheWrite: p.cacheWrite } : {}
207
+ });
208
+ } else if (event.type === "tool.completed") {
209
+ persistence.timeseries.record({
210
+ ts: Date.parse(event.timestamp) || Date.now(),
211
+ toolCalls: 1
212
+ });
213
+ }
214
+ } catch {
215
+ }
216
+ }
217
+
218
+ export {
219
+ resolveAuditActor,
220
+ TRANSCRIPT_RING_MAX,
221
+ MAX_TRANSCRIPT_SESSIONS,
222
+ MAX_AGENT_RINGS,
223
+ evictOldest,
224
+ agentRingKey,
225
+ agentMessageToEntry,
226
+ readLocalSubagentTranscript,
227
+ decodePathSegment,
228
+ decodeSessionId,
229
+ readRequestBody,
230
+ writeInvalidBody,
231
+ buildHttpUrl,
232
+ buildBootstrapHttpUrl,
233
+ buildClientWsUrl,
234
+ normalizeHqPublicOrigin,
235
+ hqRuntimeMarkerPath,
236
+ lanIPv4Addresses,
237
+ truncateHqSummary,
238
+ hqMachineKey,
239
+ recordTimeseriesSignal,
240
+ sanitizeApiError
241
+ };
242
+ //# sourceMappingURL=chunk-I7V2OYVH.js.map
@@ -1043,9 +1043,9 @@ var helpTable = {
1043
1043
  // -- HQ (dashboard server) ──────────────────────────────────────────
1044
1044
  hq: {
1045
1045
  name: "hq",
1046
- title: "wstack hq \u2014 start HQ command center or manage tokens",
1047
- description: "Start the HQ server \u2014 a web dashboard for monitoring sessions, fleet status, and agent activity across projects \u2014 or manage browser and client authentication tokens.",
1048
- usage: "wstack hq [serve] [--port <n>] [--password <secret>] [--tunnel] [--open] | wstack hq token [create|list|revoke]",
1046
+ title: "wstack hq \u2014 serve HQ, manage authentication, or install its system service",
1047
+ description: "Start the HQ server \u2014 a web dashboard for monitoring sessions, fleet status, and agent activity across projects; manage browser/client authentication, or install the persistent Ubuntu systemd service.",
1048
+ usage: "wstack hq [serve] [...] | wstack hq token [create|list|revoke] | wstack hq service [install|status|update|uninstall]",
1049
1049
  subcommands: [
1050
1050
  { name: "serve", description: "Start the HQ dashboard server (default)." },
1051
1051
  {
@@ -1053,7 +1053,17 @@ var helpTable = {
1053
1053
  description: "Mint a browser or client authentication token."
1054
1054
  },
1055
1055
  { name: "token list", description: "List active HQ authentication tokens." },
1056
- { name: "token revoke <id>", description: "Revoke an issued HQ authentication token." }
1056
+ { name: "token revoke <id>", description: "Revoke an issued HQ authentication token." },
1057
+ {
1058
+ name: "service install",
1059
+ description: "Install an always-on systemd service and guarded automatic updater."
1060
+ },
1061
+ { name: "service status", description: "Inspect the HQ service and update timer." },
1062
+ { name: "service update", description: "Run the guarded update job immediately." },
1063
+ {
1064
+ name: "service uninstall",
1065
+ description: "Remove systemd units while preserving HQ data and secrets."
1066
+ }
1057
1067
  ],
1058
1068
  seeAlso: "wstack sessions fleet (CLI equivalent for fleet status)"
1059
1069
  },
@@ -1202,4 +1212,4 @@ export {
1202
1212
  subcommandsWithFocusedHelp,
1203
1213
  renderGenericHelp
1204
1214
  };
1205
- //# sourceMappingURL=chunk-MXEJEYT7.js.map
1215
+ //# sourceMappingURL=chunk-JX6KL7OF.js.map