@wrongstack/cli 1.0.24 → 1.0.27

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 (146) hide show
  1. package/data/providers.json +44 -0
  2. package/dist/{acp-AJWSKWE5.js → acp-M4565YUW.js} +33 -20
  3. package/dist/ascii-mode-SG4PRNLP.js +7 -0
  4. package/dist/{auth-GRBZ7Y3D.js → auth-TE55JF4Z.js} +14 -14
  5. package/dist/{bench-N5K7Z5O3.js → bench-GTLPM22B.js} +2 -2
  6. package/dist/boot/append-system-prompt.d.ts +10 -0
  7. package/dist/boot/ascii-mode.d.ts +12 -0
  8. package/dist/boot/container-wiring.d.ts +4 -2
  9. package/dist/boot/dispatch-singleshot.d.ts +12 -1
  10. package/dist/boot/dispatch-webui.d.ts +2 -0
  11. package/dist/boot/goal-tui-default.d.ts +10 -0
  12. package/dist/boot/mcp-config-flag.d.ts +22 -0
  13. package/dist/boot/piped-stdin.d.ts +40 -0
  14. package/dist/boot/restricted-mode.d.ts +15 -0
  15. package/dist/boot/safe-mode.d.ts +30 -0
  16. package/dist/boot/single-shot-budget.d.ts +30 -0
  17. package/dist/boot/stream-json.d.ts +36 -0
  18. package/dist/boot/structured-output.d.ts +16 -0
  19. package/dist/boot/system-prompt-builder.d.ts +2 -0
  20. package/dist/boot/tool-restriction-flags.d.ts +36 -0
  21. package/dist/boot/worktree-sessions.d.ts +21 -0
  22. package/dist/boot-provider-selection.d.ts +67 -0
  23. package/dist/boot.d.ts +1 -38
  24. package/dist/{chunk-LFMVHT3R.js → chunk-2ERX3RPN.js} +11 -13
  25. package/dist/chunk-4TVYGG6G.js +62 -0
  26. package/dist/chunk-5JYDKG5B.js +154 -0
  27. package/dist/{chunk-3VXYY5JC.js → chunk-5UZBUV2H.js} +3 -19
  28. package/dist/chunk-CZTXF6HY.js +140 -0
  29. package/dist/{chunk-DRM7PSW7.js → chunk-ENRYW73H.js} +527 -381
  30. package/dist/{chunk-SKPDE62W.js → chunk-EPR42SWM.js} +36 -6
  31. package/dist/{chunk-YBUCXXYZ.js → chunk-F5DCFAKI.js} +1 -1
  32. package/dist/chunk-HDMFKTT3.js +157 -0
  33. package/dist/{chunk-PGKJ2OQP.js → chunk-HFZH2XJN.js} +5 -5
  34. package/dist/chunk-HLSHFXTO.js +97 -0
  35. package/dist/{chunk-QFPQXKCG.js → chunk-KOEO4CJG.js} +21 -6
  36. package/dist/chunk-LBPLTTZ6.js +105 -0
  37. package/dist/{chunk-3OCUS3VN.js → chunk-LDCGZJTC.js} +4 -4
  38. package/dist/chunk-LKPEBZBE.js +143 -0
  39. package/dist/chunk-N57IU5FG.js +58 -0
  40. package/dist/{chunk-2EBVTYJR.js → chunk-NRXWOMK7.js} +17 -3
  41. package/dist/chunk-PZQBGUVZ.js +18 -0
  42. package/dist/{chunk-MHEBWVXL.js → chunk-Q4INH2DG.js} +46 -2
  43. package/dist/{chunk-5SEYGOCH.js → chunk-RDGGWMTI.js} +2 -2
  44. package/dist/{chunk-BLD5DSVA.js → chunk-RGFWGUAB.js} +27 -15
  45. package/dist/{chunk-ZZRSG7VK.js → chunk-RZ4ZDRYA.js} +3 -3
  46. package/dist/{chunk-S4FFHDJG.js → chunk-SDJCLEK6.js} +6 -2
  47. package/dist/{chunk-U46QR4WR.js → chunk-TI7KLHYX.js} +132 -3
  48. package/dist/chunk-X756ZS72.js +329 -0
  49. package/dist/{chunk-JF2MULWF.js → chunk-YHYINL47.js} +47 -15
  50. package/dist/{cli-context-34Q5IJRA.js → cli-context-C7RBSWHF.js} +434 -327
  51. package/dist/{cli-main-AELJDBGU.js → cli-main-TQTH73ZK.js} +2948 -2315
  52. package/dist/cli-update-notice.d.ts +2 -1
  53. package/dist/config-doctor.d.ts +1 -1
  54. package/dist/{diag-doctor-WIFJH3XH.js → diag-doctor-BIZAFIAT.js} +2 -2
  55. package/dist/{execution-PYC5FCS4.js → execution-3STBD2U7.js} +2167 -1831
  56. package/dist/execution-chimera-review.d.ts +18 -1
  57. package/dist/fleet/host-types.d.ts +3 -1
  58. package/dist/goal-commands.d.ts +47 -0
  59. package/dist/goal-host.d.ts +1 -14
  60. package/dist/{hq-JTUTFI4L.js → hq-44F4OQH2.js} +9 -8
  61. package/dist/hq-fleet-control.d.ts +36 -0
  62. package/dist/hq-live-status.d.ts +22 -0
  63. package/dist/hq-publisher.d.ts +18 -0
  64. package/dist/hq-server/routes/command-handlers.d.ts +12 -1
  65. package/dist/hq-server/routes/data-handlers.d.ts +1 -1
  66. package/dist/hq-server/socket-credentials.d.ts +63 -0
  67. package/dist/hq-server/upgrade-handler.d.ts +2 -0
  68. package/dist/hq-server/ws.d.ts +24 -0
  69. package/dist/{hq-server-PC7GZMPT.js → hq-server-UIB24LU6.js} +2 -2
  70. package/dist/{hq-service-NW56MUP2.js → hq-service-BMMJAHRT.js} +4 -4
  71. package/dist/import-claude-code-AYG24RG2.js +268 -0
  72. package/dist/index.js +12 -8
  73. package/dist/live-settings-input.d.ts +1 -0
  74. package/dist/{mailbox-serve-WDOCH6KR.js → mailbox-serve-GNGXEQ2R.js} +3 -3
  75. package/dist/{modeldiag-36OGAL5C.js → modeldiag-W7XAE625.js} +9 -9
  76. package/dist/{per-subcommand-help-XE77MTGJ.js → per-subcommand-help-QDAQASPZ.js} +2 -2
  77. package/dist/permission-prompt.d.ts +14 -0
  78. package/dist/permission-rules-view.d.ts +18 -0
  79. package/dist/{permissions-CFDLXETM.js → permissions-PL27UHYD.js} +67 -45
  80. package/dist/{plugin-usage-S5LVGMBW.js → plugin-usage-CIGOOTX3.js} +2 -2
  81. package/dist/{plugins-RHBIVLI7.js → plugins-HQTXUD6A.js} +4 -2
  82. package/dist/preflight.d.ts +1 -1
  83. package/dist/{projects-LMNAGGEU.js → projects-T4CZCA25.js} +3 -6
  84. package/dist/provider-helpers.d.ts +7 -11
  85. package/dist/{providers-models-2CFG46RD.js → providers-models-5Z2PJTEW.js} +5 -5
  86. package/dist/release-asset-download.d.ts +10 -0
  87. package/dist/release-attestation.d.ts +28 -0
  88. package/dist/remote/remote-session.d.ts +64 -0
  89. package/dist/remote/remote-target.d.ts +69 -0
  90. package/dist/remote-GB5VXCCT.js +576 -0
  91. package/dist/{replay-Z3FTJEMM.js → replay-DEI566CW.js} +2 -2
  92. package/dist/services/commit-message.d.ts +2 -1
  93. package/dist/session-move-P2EC2COA.js +44 -0
  94. package/dist/session-move.d.ts +20 -0
  95. package/dist/session-stats.d.ts +10 -0
  96. package/dist/{sessions-config-IQUH2GXT.js → sessions-config-XHLXJ5C3.js} +109 -10
  97. package/dist/short-circuit-flags-5TNTJPQR.js +10 -0
  98. package/dist/slash-commands/command-context.d.ts +15 -5
  99. package/dist/slash-commands/goal-refiner.d.ts +4 -49
  100. package/dist/slash-commands/mcp.d.ts +2 -1
  101. package/dist/slash-commands/permissions.d.ts +4 -0
  102. package/dist/slash-commands/settings-context-mutations.d.ts +5 -0
  103. package/dist/slash-commands/settings-limits-mutations.d.ts +12 -0
  104. package/dist/slash-commands/theme.d.ts +15 -1
  105. package/dist/standalone-auto-update-TOTRGCSA.js +249 -0
  106. package/dist/standalone-auto-update.d.ts +65 -0
  107. package/dist/standalone-update-LZO3MNLH.js +24 -0
  108. package/dist/standalone-update.d.ts +37 -1
  109. package/dist/subcommands/handlers/import-claude-code.d.ts +52 -0
  110. package/dist/subcommands/handlers/permissions.d.ts +9 -3
  111. package/dist/subcommands/handlers/remote.d.ts +6 -0
  112. package/dist/subcommands/handlers/sessions-fleet.d.ts +0 -12
  113. package/dist/subcommands/handlers/version-help.d.ts +5 -0
  114. package/dist/{subcommands-ZTSIN7LG.js → subcommands-BHQYLDSD.js} +2 -2
  115. package/dist/tui-resume-callback.d.ts +18 -0
  116. package/dist/{typesafe-HVEVPA6R.js → typesafe-U7YB2RRV.js} +5 -5
  117. package/dist/{typesafe-topic-replay-4W73BOLV.js → typesafe-topic-replay-ZILIU3WS.js} +18 -2
  118. package/dist/{update-7XIOEXTD.js → update-TDZX4IL2.js} +4 -4
  119. package/dist/{update-check-2POMBHFB.js → update-check-J5ZVKS73.js} +3 -3
  120. package/dist/version-JB2UAV2A.js +11 -0
  121. package/dist/version-help-XJJQ7YSH.js +10 -0
  122. package/dist/webui-server/route-contexts.d.ts +1 -1
  123. package/dist/webui-server/session-start-payload.d.ts +5 -1
  124. package/dist/{webui-server-L2SICPTB.js → webui-server-GCXIO3JY.js} +515 -365
  125. package/dist/webui-server-lifecycle.d.ts +57 -0
  126. package/dist/webui-server-options.d.ts +10 -0
  127. package/dist/webui-server.d.ts +0 -14
  128. package/dist/wiring/brain-and-orchestration.d.ts +3 -1
  129. package/dist/wiring/cli-prompt-and-tools-setup.d.ts +9 -0
  130. package/dist/wiring/cli-slash-commands-setup.d.ts +1 -0
  131. package/dist/wiring/command-host-state.d.ts +2 -0
  132. package/dist/wiring/hq-telemetry.d.ts +12 -0
  133. package/dist/wiring/lifecycle-plugins.d.ts +4 -2
  134. package/dist/wiring/metrics.d.ts +9 -1
  135. package/dist/wiring/pipeline.d.ts +2 -7
  136. package/dist/wiring/plugins.d.ts +3 -0
  137. package/dist/wiring/provider-status.d.ts +1 -0
  138. package/dist/wiring/provider-utility-tools.d.ts +3 -1
  139. package/package.json +29 -28
  140. package/dist/chunk-2BKFWIER.js +0 -98
  141. package/dist/chunk-424ORSE7.js +0 -56
  142. package/dist/chunk-OWNC7LMD.js +0 -78
  143. package/dist/chunk-QBBF7GIK.js +0 -65
  144. package/dist/short-circuit-flags-SDT53OML.js +0 -10
  145. package/dist/standalone-update-MW47J3AK.js +0 -164
  146. package/dist/version-help-WZ623NWM.js +0 -10
@@ -0,0 +1,576 @@
1
+ import {
2
+ MAX_BINARY_BYTES,
3
+ downloadReleaseAssetToFile,
4
+ findStandaloneRelease,
5
+ verifyStandaloneDigest
6
+ } from "./chunk-X756ZS72.js";
7
+ import {
8
+ CLI_VERSION,
9
+ STANDALONE_TARGET
10
+ } from "./chunk-F5DCFAKI.js";
11
+ import {
12
+ openBrowser
13
+ } from "./chunk-DAKVBMD4.js";
14
+
15
+ // src/subcommands/handlers/remote.ts
16
+ import * as fsp2 from "node:fs/promises";
17
+ import * as path2 from "node:path";
18
+ import { isStandaloneBinary, toErrorMessage as toErrorMessage2, wstackGlobalRoot } from "@wrongstack/core/utils";
19
+
20
+ // src/remote/remote-session.ts
21
+ import { spawn } from "node:child_process";
22
+ import { createHash, randomBytes } from "node:crypto";
23
+ import { createReadStream, existsSync } from "node:fs";
24
+ import * as fsp from "node:fs/promises";
25
+ import * as net from "node:net";
26
+ import * as path from "node:path";
27
+ import { buildChildEnv, toErrorMessage } from "@wrongstack/core/utils";
28
+
29
+ // src/remote/remote-target.ts
30
+ function parseRemoteTarget(input) {
31
+ const target = parseRemoteTargetText(input);
32
+ if (target.destination.startsWith("-")) {
33
+ throw new Error(`A remote host cannot start with "-" (got ${target.destination})`);
34
+ }
35
+ return target;
36
+ }
37
+ function parseRemoteTargetText(input) {
38
+ const text = input.trim();
39
+ if (text.startsWith("ssh://")) {
40
+ let url;
41
+ try {
42
+ url = new URL(text);
43
+ } catch {
44
+ throw new Error(`Not a valid ssh:// address: ${text}`);
45
+ }
46
+ const user = url.username ? `${decodeURIComponent(url.username)}@` : "";
47
+ const path4 = decodeURIComponent(url.pathname);
48
+ if (!url.hostname || !path4 || path4 === "/") {
49
+ throw new Error(`Give the remote project directory: ssh://user@host/path (got ${text})`);
50
+ }
51
+ return {
52
+ destination: `${user}${url.hostname}`,
53
+ ...url.port ? { port: Number(url.port) } : {},
54
+ path: path4
55
+ };
56
+ }
57
+ const colon = text.indexOf(":");
58
+ const destination = colon > 0 ? text.slice(0, colon) : "";
59
+ const path3 = colon > 0 ? text.slice(colon + 1) : "";
60
+ if (!destination || !path3 || /\s/.test(destination)) {
61
+ throw new Error(`Give the remote as user@host:/path/to/project (got ${text || "nothing"})`);
62
+ }
63
+ return { destination, path: path3 };
64
+ }
65
+ function shQuote(value) {
66
+ return `'${value.replace(/'/g, `'\\''`)}'`;
67
+ }
68
+ function shPath(path3) {
69
+ if (path3 === "~") return '"$HOME"';
70
+ if (path3.startsWith("~/")) return `"$HOME"/${shQuote(path3.slice(2))}`;
71
+ return shQuote(path3);
72
+ }
73
+ function buildTargetFor(os, arch, musl) {
74
+ const system = os.trim().toLowerCase();
75
+ const machine = arch.trim().toLowerCase();
76
+ const cpu = machine === "x86_64" || machine === "amd64" ? "x64" : machine === "aarch64" || machine === "arm64" ? "arm64" : void 0;
77
+ if (!cpu) throw new Error(`No WrongStack build for a ${arch} remote machine.`);
78
+ if (system === "linux") return `bun-linux-${cpu}${musl ? "-musl" : ""}`;
79
+ if (system === "darwin") return `bun-darwin-${cpu}`;
80
+ throw new Error(`Remote machines running ${os} are not supported (Linux and macOS are).`);
81
+ }
82
+ var REMOTE_BIN_DIR = '"$HOME"/.wrongstack/remote';
83
+ function stateScript(path3) {
84
+ return `dir=${shPath(path3)}
85
+ abs=$(cd "$dir" 2>/dev/null && pwd -P) || abs=
86
+ key=$(printf '%s' "$abs" | cksum | tr ' ' '-')
87
+ state_dir=${REMOTE_BIN_DIR}/servers
88
+ state="$state_dir/$key.json"
89
+ token_file="$state_dir/$key.token"
90
+ log="$state_dir/$key.log"`;
91
+ }
92
+ function probeScript(path3) {
93
+ return `set -u
94
+ printf 'os=%s\\n' "$(uname -s 2>/dev/null)"
95
+ printf 'arch=%s\\n' "$(uname -m 2>/dev/null)"
96
+ if ls /lib/ld-musl-* >/dev/null 2>&1; then echo musl=1; else echo musl=0; fi
97
+ ${stateScript(path3)}
98
+ if [ -d "$dir" ]; then echo path=1; else echo path=0; fi
99
+ for f in ${REMOTE_BIN_DIR}/wstack-*; do
100
+ [ -x "$f" ] && printf 'version=%s\\n' "\${f##*/wstack-}"
101
+ done
102
+ if [ -n "$abs" ] && [ -f "$state" ] && [ -f "$token_file" ]; then
103
+ pid=$(sed -n 's/.*"pid":\\([0-9]*\\).*/\\1/p' "$state")
104
+ if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
105
+ printf 'server=%s\\n' "$(tr -d '\\n' < "$state")"
106
+ printf 'token=%s\\n' "$(cat "$token_file")"
107
+ fi
108
+ fi
109
+ `;
110
+ }
111
+ function parseProbe(output) {
112
+ const values = /* @__PURE__ */ new Map();
113
+ for (const line of output.split(/\r?\n/)) {
114
+ const eq = line.indexOf("=");
115
+ if (eq <= 0) continue;
116
+ const key = line.slice(0, eq);
117
+ values.set(key, [...values.get(key) ?? [], line.slice(eq + 1)]);
118
+ }
119
+ const one = (key) => values.get(key)?.[0] ?? "";
120
+ let server;
121
+ try {
122
+ const state = JSON.parse(one("server") || "null");
123
+ const token = one("token");
124
+ if (state && typeof state.pid === "number" && typeof state.port === "number" && typeof state.version === "string" && token) {
125
+ server = { pid: state.pid, port: state.port, version: state.version, token };
126
+ }
127
+ } catch {
128
+ server = void 0;
129
+ }
130
+ return {
131
+ os: one("os"),
132
+ arch: one("arch"),
133
+ musl: one("musl") === "1",
134
+ pathExists: one("path") === "1",
135
+ versions: values.get("version") ?? [],
136
+ ...server ? { server } : {}
137
+ };
138
+ }
139
+ function remoteBinaryPath(version) {
140
+ return `${REMOTE_BIN_DIR}/${shQuote(`wstack-${version}`)}`;
141
+ }
142
+ function installScript(version) {
143
+ const target = remoteBinaryPath(version);
144
+ return `set -eu
145
+ mkdir -p ${REMOTE_BIN_DIR}
146
+ chmod 700 ${REMOTE_BIN_DIR}
147
+ part=${REMOTE_BIN_DIR}/.upload-$$
148
+ cat > "$part"
149
+ chmod 700 "$part"
150
+ sum=$( (sha256sum "$part" 2>/dev/null || shasum -a 256 "$part") | cut -d' ' -f1)
151
+ mv -f "$part" ${target}
152
+ echo "sha256=$sum"
153
+ `;
154
+ }
155
+ var NO_PROVIDER = ["No provider or model configured", "Saved provider/model is no longer usable"];
156
+ function startScript(options) {
157
+ const bin = remoteBinaryPath(options.version);
158
+ return `set -u
159
+ umask 077
160
+ ${stateScript(options.path)}
161
+ cd "$dir" || { echo error=path; exit 3; }
162
+ mkdir -p "$state_dir"
163
+ chmod 700 "$state_dir"
164
+ printf '%s' ${shQuote(options.token)} > "$token_file"
165
+ export WEBUI_TOKEN=${shQuote(options.token)}
166
+ launch() {
167
+ : > "$log"
168
+ if command -v setsid >/dev/null 2>&1; then
169
+ nohup setsid ${bin} --webui --host 127.0.0.1 --port ${options.preferredPort} "$@" > "$log" 2>&1 < /dev/null &
170
+ else
171
+ nohup ${bin} --webui --host 127.0.0.1 --port ${options.preferredPort} "$@" > "$log" 2>&1 < /dev/null &
172
+ fi
173
+ pid=$!
174
+ }
175
+ # 0: running and "port" is set, 1: the host exited, 2: no port in time.
176
+ await_port() {
177
+ i=0
178
+ while [ "$i" -lt 240 ]; do
179
+ port=$(sed -n 's/.*WebUI running.*127\\.0\\.0\\.1:\\([0-9][0-9]*\\).*/\\1/p' "$log" | head -n 1)
180
+ [ -n "$port" ] && return 0
181
+ kill -0 "$pid" 2>/dev/null || return 1
182
+ sleep 0.5
183
+ i=$((i + 1))
184
+ done
185
+ return 2
186
+ }
187
+ launch
188
+ await_port
189
+ status=$?
190
+ if [ "$status" -eq 1 ] && grep -q ${NO_PROVIDER.map((line) => `-e ${shQuote(line)}`).join(" ")} "$log"; then
191
+ echo setup=1
192
+ launch --provider wrongstack-setup --model no-api-key
193
+ await_port
194
+ status=$?
195
+ fi
196
+ if [ "$status" -eq 0 ]; then
197
+ printf '{"pid":%s,"port":%s,"version":"%s"}\\n' "$pid" "$port" ${shQuote(options.version)} > "$state"
198
+ echo "port=$port"
199
+ echo "pid=$pid"
200
+ exit 0
201
+ fi
202
+ rm -f "$token_file"
203
+ if [ "$status" -eq 1 ]; then
204
+ echo error=exited
205
+ tail -n 30 "$log" | sed 's/^/log=/'
206
+ exit 4
207
+ fi
208
+ kill "$pid" 2>/dev/null
209
+ echo error=timeout
210
+ exit 5
211
+ `;
212
+ }
213
+ function stopScript(path3) {
214
+ return `set -u
215
+ ${stateScript(path3)}
216
+ [ -n "$abs" ] && [ -f "$state" ] || exit 0
217
+ pid=$(sed -n 's/.*"pid":\\([0-9]*\\).*/\\1/p' "$state")
218
+ [ -n "$pid" ] && kill "$pid" 2>/dev/null
219
+ rm -f "$state" "$token_file"
220
+ echo stopped=1
221
+ `;
222
+ }
223
+
224
+ // src/remote/remote-session.ts
225
+ var RECONNECT_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3, 3e4];
226
+ var READY_TIMEOUT_MS = 3e4;
227
+ async function sha256File(file) {
228
+ const hash = createHash("sha256");
229
+ for await (const chunk of createReadStream(file)) hash.update(chunk);
230
+ return hash.digest("hex");
231
+ }
232
+ async function freeLocalPort(start) {
233
+ for (let port = start; port < start + 200; port++) {
234
+ const free = await new Promise((resolve2) => {
235
+ const server = net.createServer();
236
+ server.once("error", () => resolve2(false));
237
+ server.listen(port, "127.0.0.1", () => server.close(() => resolve2(true)));
238
+ });
239
+ if (free) return port;
240
+ }
241
+ throw new Error(`No free local port from ${start} to ${start + 199}.`);
242
+ }
243
+ var REMOTE_ERRORS = {
244
+ path: "cannot enter the project directory",
245
+ exited: "the WrongStack host exited",
246
+ timeout: "the WrongStack host did not report its port within two minutes"
247
+ };
248
+ function sshError(step, result) {
249
+ const lines = result.stdout.split(/\r?\n/);
250
+ const code = lines.find((line) => line.startsWith("error="))?.slice("error=".length);
251
+ const log = lines.filter((line) => line.startsWith("log=")).map((line) => line.slice(4));
252
+ const reason = code ? REMOTE_ERRORS[code] ?? code : `ssh exited with ${result.code}`;
253
+ const detail = log.length > 0 ? `:
254
+ ${log.join("\n")}` : code ? "." : ". See the ssh message above.";
255
+ return new Error(`Remote ${step} failed: ${reason}${detail}`);
256
+ }
257
+ async function ensureBinary(opts, probe) {
258
+ const buildTarget = buildTargetFor(probe.os, probe.arch, probe.musl);
259
+ const binary = await opts.resolveBinary(buildTarget, probe.versions);
260
+ if (probe.versions.includes(binary.version)) return binary;
261
+ opts.log(
262
+ `Uploading WrongStack ${binary.version} (${buildTarget}) to ${opts.target.destination}\u2026`
263
+ );
264
+ const sent = await sha256File(binary.file);
265
+ const result = await opts.ssh(
266
+ `sh -c ${shQuote(installScript(binary.version))}`,
267
+ createReadStream(binary.file)
268
+ );
269
+ const received = /^sha256=([a-f0-9]{64})$/m.exec(result.stdout)?.[1];
270
+ if (result.code !== 0 || !received) throw sshError("upload", result);
271
+ if (received !== sent) {
272
+ throw new Error("The build that arrived on the remote machine does not match the one sent.");
273
+ }
274
+ return binary;
275
+ }
276
+ async function ensureServer(opts, probe, binary) {
277
+ if (probe.server && probe.server.version === binary.version) {
278
+ opts.log(`Reusing the WrongStack host already running on ${opts.target.destination}.`);
279
+ return { port: probe.server.port, token: probe.server.token };
280
+ }
281
+ if (probe.server) await opts.ssh("sh -s", stopScript(opts.target.path));
282
+ const token = randomBytes(24).toString("base64url");
283
+ opts.log(`Starting WrongStack on ${opts.target.destination}:${opts.target.path}\u2026`);
284
+ const result = await opts.ssh(
285
+ "sh -s",
286
+ startScript({ path: opts.target.path, version: binary.version, token, preferredPort: 3456 })
287
+ );
288
+ if (/^setup=1$/m.test(result.stdout)) {
289
+ opts.log("No usable provider is set up there yet; choose one in the WebUI.");
290
+ }
291
+ const port = Number(/^port=(\d+)$/m.exec(result.stdout)?.[1]);
292
+ if (result.code !== 0 || !Number.isInteger(port) || port <= 0) {
293
+ throw sshError("start", result);
294
+ }
295
+ return { port, token };
296
+ }
297
+ async function probeRemote(opts) {
298
+ const result = await opts.ssh("sh -s", probeScript(opts.target.path));
299
+ if (result.code !== 0) throw sshError("connection", result);
300
+ const probe = parseProbe(result.stdout);
301
+ if (!probe.pathExists) {
302
+ throw new Error(`${opts.target.path} does not exist on ${opts.target.destination}.`);
303
+ }
304
+ return probe;
305
+ }
306
+ async function waitForWebui(localPort, token, tunnel, signal, timeoutMs) {
307
+ const deadline = Date.now() + timeoutMs;
308
+ while (Date.now() < deadline && tunnel.exitCode === null && !signal.aborted) {
309
+ try {
310
+ const res = await fetch(`http://127.0.0.1:${localPort}/ws-auth`, {
311
+ headers: { "X-WS-Token": token },
312
+ signal: AbortSignal.timeout(3e3)
313
+ });
314
+ if (res.ok) return true;
315
+ } catch {
316
+ }
317
+ await new Promise((resolve2) => setTimeout(resolve2, 400));
318
+ }
319
+ return false;
320
+ }
321
+ function exited(child) {
322
+ return child.exitCode !== null ? Promise.resolve(child.exitCode) : new Promise((resolve2) => child.once("exit", (code) => resolve2(code ?? 1)));
323
+ }
324
+ async function runRemoteSession(opts) {
325
+ const sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
326
+ let probe = await probeRemote(opts);
327
+ const binary = await ensureBinary(opts, probe);
328
+ let server = await ensureServer(opts, probe, binary);
329
+ const localPort = await freeLocalPort(opts.localPort);
330
+ let announced = "";
331
+ let failures = 0;
332
+ try {
333
+ while (!opts.signal.aborted) {
334
+ const tunnel = opts.tunnel(localPort, server.port);
335
+ const stopTunnel = () => tunnel.kill();
336
+ opts.signal.addEventListener("abort", stopTunnel, { once: true });
337
+ const ready = await waitForWebui(
338
+ localPort,
339
+ server.token,
340
+ tunnel,
341
+ opts.signal,
342
+ opts.readyTimeoutMs ?? READY_TIMEOUT_MS
343
+ );
344
+ if (!ready && !opts.signal.aborted) {
345
+ tunnel.kill();
346
+ if (!announced) {
347
+ await exited(tunnel);
348
+ opts.signal.removeEventListener("abort", stopTunnel);
349
+ throw new Error(
350
+ `The WebUI on ${opts.target.destination} did not answer through the SSH tunnel. Check that its sshd allows port forwarding (AllowTcpForwarding) and see the host log under ~/.wrongstack/remote/servers/ there.`
351
+ );
352
+ }
353
+ }
354
+ if (ready) {
355
+ failures = 0;
356
+ const url = `http://127.0.0.1:${localPort}/?token=${server.token}`;
357
+ if (url !== announced) {
358
+ opts.log(`WebUI for ${opts.target.destination}:${opts.target.path} \u2192 ${url}`);
359
+ opts.log("Press Ctrl+C to disconnect.");
360
+ if (opts.open && !announced) opts.openUrl?.(url);
361
+ announced = url;
362
+ } else {
363
+ opts.log("Reconnected.");
364
+ }
365
+ }
366
+ await exited(tunnel);
367
+ opts.signal.removeEventListener("abort", stopTunnel);
368
+ if (opts.signal.aborted) break;
369
+ const delay = RECONNECT_DELAYS_MS[Math.min(failures, RECONNECT_DELAYS_MS.length - 1)] ?? 3e4;
370
+ failures += 1;
371
+ opts.log(
372
+ `The connection to ${opts.target.destination} dropped; reconnecting in ${delay / 1e3}s\u2026`
373
+ );
374
+ await sleep(delay);
375
+ if (opts.signal.aborted) break;
376
+ try {
377
+ probe = await probeRemote(opts);
378
+ server = await ensureServer(opts, probe, binary);
379
+ } catch (err) {
380
+ opts.log(toErrorMessage(err));
381
+ }
382
+ }
383
+ } finally {
384
+ if (!opts.keep) {
385
+ await opts.ssh("sh -s", stopScript(opts.target.path)).then(() => opts.log(`Stopped WrongStack on ${opts.target.destination}.`)).catch(() => opts.log("Could not stop the remote WrongStack host; it keeps running."));
386
+ } else {
387
+ opts.log(`WrongStack keeps running on ${opts.target.destination} (--keep).`);
388
+ }
389
+ }
390
+ return 0;
391
+ }
392
+ function sshAgentEnv() {
393
+ const env = {};
394
+ for (const key of [
395
+ "SSH_AUTH_SOCK",
396
+ "SSH_AGENT_PID",
397
+ "SSH_ASKPASS",
398
+ "SSH_ASKPASS_REQUIRE",
399
+ "DISPLAY"
400
+ ]) {
401
+ const value = process.env[key];
402
+ if (value !== void 0) env[key] = value;
403
+ }
404
+ return env;
405
+ }
406
+ function sshCommandLine(target, options) {
407
+ return [
408
+ ...options.sshConfig ? ["-F", options.sshConfig] : [],
409
+ ...target.port ? ["-p", String(target.port)] : [],
410
+ // Without a terminal (or an askpass helper) a password prompt would hang.
411
+ ...options.batch ? ["-o", "BatchMode=yes"] : [],
412
+ "-o",
413
+ "ConnectTimeout=20",
414
+ "-o",
415
+ "ServerAliveInterval=15",
416
+ "-o",
417
+ "ServerAliveCountMax=3"
418
+ ];
419
+ }
420
+ function systemSsh(target, options) {
421
+ const base = sshCommandLine(target, options);
422
+ const run = (remoteCommand, input) => new Promise((resolve2) => {
423
+ const child = spawn("ssh", [...base, "--", target.destination, remoteCommand], {
424
+ stdio: ["pipe", "pipe", "inherit"],
425
+ env: { ...buildChildEnv(), ...sshAgentEnv() },
426
+ windowsHide: true
427
+ });
428
+ let stdout = "";
429
+ child.stdout?.setEncoding("utf8");
430
+ child.stdout?.on("data", (chunk) => {
431
+ if (stdout.length < 1e6) stdout += chunk;
432
+ });
433
+ child.on("error", (err) => resolve2({ code: 127, stdout: `error=${err.message}` }));
434
+ child.on("close", (code) => resolve2({ code: code ?? 1, stdout }));
435
+ child.stdin?.on("error", () => void 0);
436
+ if (typeof input === "string") child.stdin?.end(input);
437
+ else input.pipe(child.stdin);
438
+ });
439
+ const tunnel = (localPort, remotePort) => spawn(
440
+ "ssh",
441
+ [
442
+ ...base,
443
+ "-N",
444
+ "-o",
445
+ "ExitOnForwardFailure=yes",
446
+ "-L",
447
+ `127.0.0.1:${localPort}:127.0.0.1:${remotePort}`,
448
+ "--",
449
+ target.destination
450
+ ],
451
+ {
452
+ stdio: ["ignore", "ignore", "inherit"],
453
+ env: { ...buildChildEnv(), ...sshAgentEnv() },
454
+ windowsHide: true
455
+ }
456
+ );
457
+ return { run, tunnel };
458
+ }
459
+ function releaseCacheFile(root, version, assetName) {
460
+ return path.join(root, "remote-builds", `${version}-${assetName}`);
461
+ }
462
+ async function cachedFile(file) {
463
+ if (!existsSync(file)) return void 0;
464
+ const stat2 = await fsp.stat(file);
465
+ return stat2.size > 0 ? file : void 0;
466
+ }
467
+
468
+ // src/subcommands/handlers/remote.ts
469
+ var USAGE = `Usage: wstack remote <user@host:/path | ssh://user@host:port/path> [options]
470
+
471
+ Runs WrongStack on the remote machine, in that project directory, and opens its
472
+ WebUI here through an SSH tunnel. The agent, its tools and its daemons run
473
+ remotely. The system ssh is used, so ~/.ssh/config, your agent and known_hosts
474
+ apply. Nothing from your local configuration is copied; set up a provider in the
475
+ remote WebUI the first time.
476
+
477
+ Options:
478
+ --port <n> Local port for the WebUI (default 3456, next free one)
479
+ --open Open the WebUI in your browser
480
+ --keep Leave WrongStack running remotely when you disconnect
481
+ --ssh-config <file> Use this ssh config file (ssh -F)
482
+ --remote-binary <f> Upload this WrongStack build instead of the release one
483
+ `;
484
+ function flagString(flags, name) {
485
+ const value = flags[name];
486
+ return typeof value === "string" && value ? value : void 0;
487
+ }
488
+ async function resolveBinary(buildTarget, installed, explicit, log) {
489
+ if (explicit) {
490
+ const file2 = path2.resolve(explicit);
491
+ const sha2 = await sha256File(file2);
492
+ return { file: file2, version: `build-${sha2.slice(0, 16)}` };
493
+ }
494
+ if (isStandaloneBinary() && STANDALONE_TARGET === buildTarget) {
495
+ return { file: process.execPath, version: CLI_VERSION };
496
+ }
497
+ const release = await findStandaloneRelease(buildTarget);
498
+ if (installed.includes(release.version)) return { file: "", version: release.version };
499
+ const file = releaseCacheFile(wstackGlobalRoot(), release.version, release.assetName);
500
+ const cached = await cachedFile(file);
501
+ if (cached) {
502
+ await verifyStandaloneDigest(release, await sha256File(cached));
503
+ return { file: cached, version: release.version };
504
+ }
505
+ log(`Downloading WrongStack ${release.version} for ${buildTarget}\u2026`);
506
+ await fsp2.mkdir(path2.dirname(file), { recursive: true });
507
+ await fsp2.rm(file, { force: true });
508
+ const sha = await downloadReleaseAssetToFile(release.asset.browser_download_url, file, {
509
+ timeoutMs: 10 * 6e4,
510
+ maxBytes: MAX_BINARY_BYTES
511
+ });
512
+ try {
513
+ await verifyStandaloneDigest(release, sha);
514
+ } catch (err) {
515
+ await fsp2.rm(file, { force: true });
516
+ throw err;
517
+ }
518
+ return { file, version: release.version };
519
+ }
520
+ async function runRemoteCommand(args, flags, renderer) {
521
+ const write = (line) => renderer.writeLine(line);
522
+ const targetText = flagString(flags, "remote") ?? args[0];
523
+ if (flags["help"] === true || !targetText) {
524
+ write(USAGE);
525
+ return flags["help"] === true ? 0 : 2;
526
+ }
527
+ let target;
528
+ try {
529
+ target = parseRemoteTarget(targetText);
530
+ } catch (err) {
531
+ write(toErrorMessage2(err));
532
+ return 2;
533
+ }
534
+ const portText = flagString(flags, "port");
535
+ const localPort = portText ? Number(portText) : 3456;
536
+ if (!Number.isInteger(localPort) || localPort < 1 || localPort > 65535) {
537
+ write(`--port must be a TCP port, got ${portText}`);
538
+ return 2;
539
+ }
540
+ const sshConfig = flagString(flags, "ssh-config");
541
+ const explicitBinary = flagString(flags, "remote-binary");
542
+ const ssh = systemSsh(target, {
543
+ ...sshConfig ? { sshConfig } : {},
544
+ batch: !process.stdin.isTTY && !process.env["SSH_ASKPASS"]
545
+ });
546
+ const controller = new AbortController();
547
+ const onInterrupt = () => controller.abort();
548
+ process.once("SIGINT", onInterrupt);
549
+ process.once("SIGTERM", onInterrupt);
550
+ try {
551
+ return await runRemoteSession({
552
+ target,
553
+ localPort,
554
+ keep: flags["keep"] === true,
555
+ open: flags["open"] === true,
556
+ signal: controller.signal,
557
+ log: write,
558
+ ssh: ssh.run,
559
+ tunnel: ssh.tunnel,
560
+ resolveBinary: (buildTarget, installed) => resolveBinary(buildTarget, installed, explicitBinary, write),
561
+ openUrl: openBrowser
562
+ });
563
+ } catch (err) {
564
+ write(toErrorMessage2(err));
565
+ return 1;
566
+ } finally {
567
+ process.off("SIGINT", onInterrupt);
568
+ process.off("SIGTERM", onInterrupt);
569
+ }
570
+ }
571
+ var remoteCmd = (args, deps) => runRemoteCommand(args, deps.flags ?? {}, deps.renderer);
572
+ export {
573
+ remoteCmd,
574
+ runRemoteCommand
575
+ };
576
+ //# sourceMappingURL=remote-GB5VXCCT.js.map
@@ -25,9 +25,9 @@ function bindReplayToContainer(opts) {
25
25
  warn: (m) => logger.warn?.(m)
26
26
  } : void 0
27
27
  });
28
- container.bind(TOKENS.ProviderRunner, () => wrapped);
28
+ container.override(TOKENS.ProviderRunner, () => wrapped);
29
29
  }
30
30
  export {
31
31
  bindReplayToContainer
32
32
  };
33
- //# sourceMappingURL=replay-Z3FTJEMM.js.map
33
+ //# sourceMappingURL=replay-DEI566CW.js.map
@@ -16,7 +16,8 @@ export interface CommitLLMProvider {
16
16
  text: string;
17
17
  }[];
18
18
  }[];
19
- maxTokens: number;
19
+ /** Unset = the model's own output ceiling. */
20
+ maxTokens?: number | undefined;
20
21
  temperature?: number | undefined;
21
22
  }, opts: {
22
23
  signal: AbortSignal;
@@ -0,0 +1,44 @@
1
+
2
+ // src/session-move.ts
3
+ import * as fs from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import { DefaultSessionStore } from "@wrongstack/core/storage";
6
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
7
+ async function moveSessionTo(opts) {
8
+ const { store } = opts;
9
+ if (!store.move || store.sessionsDir === void 0) {
10
+ throw new Error("This session store cannot move sessions.");
11
+ }
12
+ const checkout = path.resolve(opts.targetPath);
13
+ const stat2 = await fs.stat(checkout).catch(() => null);
14
+ if (!stat2?.isDirectory()) throw new Error(`Not a directory: ${checkout}`);
15
+ const targetDir = resolveWstackPaths({
16
+ projectRoot: checkout,
17
+ ...opts.globalRoot ? { globalRoot: opts.globalRoot } : {}
18
+ }).projectSessions;
19
+ const adopt = store.adoptMovedSession?.bind(store);
20
+ if (path.resolve(targetDir) === path.resolve(store.sessionsDir) && adopt) {
21
+ return store.move(opts.sessionId, {
22
+ store: { sessionsDir: store.sessionsDir, adoptMovedSession: adopt },
23
+ checkout
24
+ });
25
+ }
26
+ await fs.mkdir(targetDir, { recursive: true });
27
+ const targetStore = new DefaultSessionStore({ dir: targetDir, projectRoot: checkout });
28
+ try {
29
+ return await store.move(opts.sessionId, { store: targetStore, checkout });
30
+ } finally {
31
+ await targetStore.dispose().catch(() => void 0);
32
+ }
33
+ }
34
+ function describeSessionMove(result) {
35
+ if (result.kind === "worktree") {
36
+ return `Moved ${result.id} to the worktree at ${result.checkout}; /resume lists it there.`;
37
+ }
38
+ return `Moved ${result.id} to the project at ${result.checkout}${result.fromProject ? ` (from ${result.fromProject})` : ""}. Open it there with: cd "${result.checkout}" && wstack resume ${result.id}`;
39
+ }
40
+ export {
41
+ describeSessionMove,
42
+ moveSessionTo
43
+ };
44
+ //# sourceMappingURL=session-move-P2EC2COA.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `sessions move` for the CLI subcommand and the `/sessions move` slash
3
+ * command: resolve where a session goes and hand both stores to
4
+ * `SessionStore.move`.
5
+ *
6
+ * A path inside another git worktree of this repository resolves to this
7
+ * store (all worktrees share one), so the move only re-stamps the session's
8
+ * checkout. Any other path is a project of its own: its store is opened here,
9
+ * which connects to that project's session catalog.
10
+ */
11
+ import type { SessionMoveResult, SessionStore } from '@wrongstack/core/types';
12
+ export declare function moveSessionTo(opts: {
13
+ store: SessionStore;
14
+ sessionId: string;
15
+ targetPath: string;
16
+ globalRoot?: string | undefined;
17
+ }): Promise<SessionMoveResult>;
18
+ /** One line for the user: where the session went and how to open it there. */
19
+ export declare function describeSessionMove(result: SessionMoveResult): string;
20
+ //# sourceMappingURL=session-move.d.ts.map
@@ -21,6 +21,16 @@ export declare class SessionStats {
21
21
  private bytesWritten;
22
22
  private bashCommands;
23
23
  private fetches;
24
+ /**
25
+ * First token after submit: from a leader turn's start to its first
26
+ * streamed text, thinking or tool call — what the user actually waits for,
27
+ * the provider's latency plus everything the agent does before the request.
28
+ * The leader is the context of the first run; subagent runs are not turns
29
+ * anyone typed.
30
+ */
31
+ private leaderCtx;
32
+ private turnStartedAt;
33
+ private readonly firstTokenMs;
24
34
  private readonly _handlers;
25
35
  constructor(events: EventBus, tokenCounter: TokenCounter);
26
36
  /** Remove all event listeners. Call this when the session ends. */