@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.
- package/data/providers.json +44 -0
- package/dist/{acp-AJWSKWE5.js → acp-M4565YUW.js} +33 -20
- package/dist/ascii-mode-SG4PRNLP.js +7 -0
- package/dist/{auth-GRBZ7Y3D.js → auth-TE55JF4Z.js} +14 -14
- package/dist/{bench-N5K7Z5O3.js → bench-GTLPM22B.js} +2 -2
- package/dist/boot/append-system-prompt.d.ts +10 -0
- package/dist/boot/ascii-mode.d.ts +12 -0
- package/dist/boot/container-wiring.d.ts +4 -2
- package/dist/boot/dispatch-singleshot.d.ts +12 -1
- package/dist/boot/dispatch-webui.d.ts +2 -0
- package/dist/boot/goal-tui-default.d.ts +10 -0
- package/dist/boot/mcp-config-flag.d.ts +22 -0
- package/dist/boot/piped-stdin.d.ts +40 -0
- package/dist/boot/restricted-mode.d.ts +15 -0
- package/dist/boot/safe-mode.d.ts +30 -0
- package/dist/boot/single-shot-budget.d.ts +30 -0
- package/dist/boot/stream-json.d.ts +36 -0
- package/dist/boot/structured-output.d.ts +16 -0
- package/dist/boot/system-prompt-builder.d.ts +2 -0
- package/dist/boot/tool-restriction-flags.d.ts +36 -0
- package/dist/boot/worktree-sessions.d.ts +21 -0
- package/dist/boot-provider-selection.d.ts +67 -0
- package/dist/boot.d.ts +1 -38
- package/dist/{chunk-LFMVHT3R.js → chunk-2ERX3RPN.js} +11 -13
- package/dist/chunk-4TVYGG6G.js +62 -0
- package/dist/chunk-5JYDKG5B.js +154 -0
- package/dist/{chunk-3VXYY5JC.js → chunk-5UZBUV2H.js} +3 -19
- package/dist/chunk-CZTXF6HY.js +140 -0
- package/dist/{chunk-DRM7PSW7.js → chunk-ENRYW73H.js} +527 -381
- package/dist/{chunk-SKPDE62W.js → chunk-EPR42SWM.js} +36 -6
- package/dist/{chunk-YBUCXXYZ.js → chunk-F5DCFAKI.js} +1 -1
- package/dist/chunk-HDMFKTT3.js +157 -0
- package/dist/{chunk-PGKJ2OQP.js → chunk-HFZH2XJN.js} +5 -5
- package/dist/chunk-HLSHFXTO.js +97 -0
- package/dist/{chunk-QFPQXKCG.js → chunk-KOEO4CJG.js} +21 -6
- package/dist/chunk-LBPLTTZ6.js +105 -0
- package/dist/{chunk-3OCUS3VN.js → chunk-LDCGZJTC.js} +4 -4
- package/dist/chunk-LKPEBZBE.js +143 -0
- package/dist/chunk-N57IU5FG.js +58 -0
- package/dist/{chunk-2EBVTYJR.js → chunk-NRXWOMK7.js} +17 -3
- package/dist/chunk-PZQBGUVZ.js +18 -0
- package/dist/{chunk-MHEBWVXL.js → chunk-Q4INH2DG.js} +46 -2
- package/dist/{chunk-5SEYGOCH.js → chunk-RDGGWMTI.js} +2 -2
- package/dist/{chunk-BLD5DSVA.js → chunk-RGFWGUAB.js} +27 -15
- package/dist/{chunk-ZZRSG7VK.js → chunk-RZ4ZDRYA.js} +3 -3
- package/dist/{chunk-S4FFHDJG.js → chunk-SDJCLEK6.js} +6 -2
- package/dist/{chunk-U46QR4WR.js → chunk-TI7KLHYX.js} +132 -3
- package/dist/chunk-X756ZS72.js +329 -0
- package/dist/{chunk-JF2MULWF.js → chunk-YHYINL47.js} +47 -15
- package/dist/{cli-context-34Q5IJRA.js → cli-context-C7RBSWHF.js} +434 -327
- package/dist/{cli-main-AELJDBGU.js → cli-main-TQTH73ZK.js} +2948 -2315
- package/dist/cli-update-notice.d.ts +2 -1
- package/dist/config-doctor.d.ts +1 -1
- package/dist/{diag-doctor-WIFJH3XH.js → diag-doctor-BIZAFIAT.js} +2 -2
- package/dist/{execution-PYC5FCS4.js → execution-3STBD2U7.js} +2167 -1831
- package/dist/execution-chimera-review.d.ts +18 -1
- package/dist/fleet/host-types.d.ts +3 -1
- package/dist/goal-commands.d.ts +47 -0
- package/dist/goal-host.d.ts +1 -14
- package/dist/{hq-JTUTFI4L.js → hq-44F4OQH2.js} +9 -8
- package/dist/hq-fleet-control.d.ts +36 -0
- package/dist/hq-live-status.d.ts +22 -0
- package/dist/hq-publisher.d.ts +18 -0
- package/dist/hq-server/routes/command-handlers.d.ts +12 -1
- package/dist/hq-server/routes/data-handlers.d.ts +1 -1
- package/dist/hq-server/socket-credentials.d.ts +63 -0
- package/dist/hq-server/upgrade-handler.d.ts +2 -0
- package/dist/hq-server/ws.d.ts +24 -0
- package/dist/{hq-server-PC7GZMPT.js → hq-server-UIB24LU6.js} +2 -2
- package/dist/{hq-service-NW56MUP2.js → hq-service-BMMJAHRT.js} +4 -4
- package/dist/import-claude-code-AYG24RG2.js +268 -0
- package/dist/index.js +12 -8
- package/dist/live-settings-input.d.ts +1 -0
- package/dist/{mailbox-serve-WDOCH6KR.js → mailbox-serve-GNGXEQ2R.js} +3 -3
- package/dist/{modeldiag-36OGAL5C.js → modeldiag-W7XAE625.js} +9 -9
- package/dist/{per-subcommand-help-XE77MTGJ.js → per-subcommand-help-QDAQASPZ.js} +2 -2
- package/dist/permission-prompt.d.ts +14 -0
- package/dist/permission-rules-view.d.ts +18 -0
- package/dist/{permissions-CFDLXETM.js → permissions-PL27UHYD.js} +67 -45
- package/dist/{plugin-usage-S5LVGMBW.js → plugin-usage-CIGOOTX3.js} +2 -2
- package/dist/{plugins-RHBIVLI7.js → plugins-HQTXUD6A.js} +4 -2
- package/dist/preflight.d.ts +1 -1
- package/dist/{projects-LMNAGGEU.js → projects-T4CZCA25.js} +3 -6
- package/dist/provider-helpers.d.ts +7 -11
- package/dist/{providers-models-2CFG46RD.js → providers-models-5Z2PJTEW.js} +5 -5
- package/dist/release-asset-download.d.ts +10 -0
- package/dist/release-attestation.d.ts +28 -0
- package/dist/remote/remote-session.d.ts +64 -0
- package/dist/remote/remote-target.d.ts +69 -0
- package/dist/remote-GB5VXCCT.js +576 -0
- package/dist/{replay-Z3FTJEMM.js → replay-DEI566CW.js} +2 -2
- package/dist/services/commit-message.d.ts +2 -1
- package/dist/session-move-P2EC2COA.js +44 -0
- package/dist/session-move.d.ts +20 -0
- package/dist/session-stats.d.ts +10 -0
- package/dist/{sessions-config-IQUH2GXT.js → sessions-config-XHLXJ5C3.js} +109 -10
- package/dist/short-circuit-flags-5TNTJPQR.js +10 -0
- package/dist/slash-commands/command-context.d.ts +15 -5
- package/dist/slash-commands/goal-refiner.d.ts +4 -49
- package/dist/slash-commands/mcp.d.ts +2 -1
- package/dist/slash-commands/permissions.d.ts +4 -0
- package/dist/slash-commands/settings-context-mutations.d.ts +5 -0
- package/dist/slash-commands/settings-limits-mutations.d.ts +12 -0
- package/dist/slash-commands/theme.d.ts +15 -1
- package/dist/standalone-auto-update-TOTRGCSA.js +249 -0
- package/dist/standalone-auto-update.d.ts +65 -0
- package/dist/standalone-update-LZO3MNLH.js +24 -0
- package/dist/standalone-update.d.ts +37 -1
- package/dist/subcommands/handlers/import-claude-code.d.ts +52 -0
- package/dist/subcommands/handlers/permissions.d.ts +9 -3
- package/dist/subcommands/handlers/remote.d.ts +6 -0
- package/dist/subcommands/handlers/sessions-fleet.d.ts +0 -12
- package/dist/subcommands/handlers/version-help.d.ts +5 -0
- package/dist/{subcommands-ZTSIN7LG.js → subcommands-BHQYLDSD.js} +2 -2
- package/dist/tui-resume-callback.d.ts +18 -0
- package/dist/{typesafe-HVEVPA6R.js → typesafe-U7YB2RRV.js} +5 -5
- package/dist/{typesafe-topic-replay-4W73BOLV.js → typesafe-topic-replay-ZILIU3WS.js} +18 -2
- package/dist/{update-7XIOEXTD.js → update-TDZX4IL2.js} +4 -4
- package/dist/{update-check-2POMBHFB.js → update-check-J5ZVKS73.js} +3 -3
- package/dist/version-JB2UAV2A.js +11 -0
- package/dist/version-help-XJJQ7YSH.js +10 -0
- package/dist/webui-server/route-contexts.d.ts +1 -1
- package/dist/webui-server/session-start-payload.d.ts +5 -1
- package/dist/{webui-server-L2SICPTB.js → webui-server-GCXIO3JY.js} +515 -365
- package/dist/webui-server-lifecycle.d.ts +57 -0
- package/dist/webui-server-options.d.ts +10 -0
- package/dist/webui-server.d.ts +0 -14
- package/dist/wiring/brain-and-orchestration.d.ts +3 -1
- package/dist/wiring/cli-prompt-and-tools-setup.d.ts +9 -0
- package/dist/wiring/cli-slash-commands-setup.d.ts +1 -0
- package/dist/wiring/command-host-state.d.ts +2 -0
- package/dist/wiring/hq-telemetry.d.ts +12 -0
- package/dist/wiring/lifecycle-plugins.d.ts +4 -2
- package/dist/wiring/metrics.d.ts +9 -1
- package/dist/wiring/pipeline.d.ts +2 -7
- package/dist/wiring/plugins.d.ts +3 -0
- package/dist/wiring/provider-status.d.ts +1 -0
- package/dist/wiring/provider-utility-tools.d.ts +3 -1
- package/package.json +29 -28
- package/dist/chunk-2BKFWIER.js +0 -98
- package/dist/chunk-424ORSE7.js +0 -56
- package/dist/chunk-OWNC7LMD.js +0 -78
- package/dist/chunk-QBBF7GIK.js +0 -65
- package/dist/short-circuit-flags-SDT53OML.js +0 -10
- package/dist/standalone-update-MW47J3AK.js +0 -164
- 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.
|
|
28
|
+
container.override(TOKENS.ProviderRunner, () => wrapped);
|
|
29
29
|
}
|
|
30
30
|
export {
|
|
31
31
|
bindReplayToContainer
|
|
32
32
|
};
|
|
33
|
-
//# sourceMappingURL=replay-
|
|
33
|
+
//# sourceMappingURL=replay-DEI566CW.js.map
|
|
@@ -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
|
package/dist/session-stats.d.ts
CHANGED
|
@@ -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. */
|