litmus-cli 1.4.22 → 1.4.25
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/README.md +30 -0
- package/dist/commands/connect.d.ts +231 -2
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +311 -12
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +16 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/pause.d.ts +36 -0
- package/dist/commands/pause.d.ts.map +1 -0
- package/dist/commands/pause.js +110 -0
- package/dist/commands/pause.js.map +1 -0
- package/dist/commands/push.d.ts +7 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +40 -1
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/setup-check.d.ts +19 -0
- package/dist/commands/setup-check.d.ts.map +1 -0
- package/dist/commands/setup-check.js +48 -0
- package/dist/commands/setup-check.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +6 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/submit.d.ts.map +1 -1
- package/dist/commands/submit.js +18 -0
- package/dist/commands/submit.js.map +1 -1
- package/dist/index.js +38 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +27 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +45 -0
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/environment-checks.d.ts +123 -0
- package/dist/lib/environment-checks.d.ts.map +1 -0
- package/dist/lib/environment-checks.js +324 -0
- package/dist/lib/environment-checks.js.map +1 -0
- package/dist/lib/git-bundle.d.ts +19 -0
- package/dist/lib/git-bundle.d.ts.map +1 -0
- package/dist/lib/git-bundle.js +71 -0
- package/dist/lib/git-bundle.js.map +1 -0
- package/dist/lib/session-end.d.ts +111 -0
- package/dist/lib/session-end.d.ts.map +1 -0
- package/dist/lib/session-end.js +126 -0
- package/dist/lib/session-end.js.map +1 -0
- package/dist/lib/submit-route.d.ts +68 -0
- package/dist/lib/submit-route.d.ts.map +1 -0
- package/dist/lib/submit-route.js +73 -0
- package/dist/lib/submit-route.js.map +1 -0
- package/dist/lib/watcher.js +23 -2
- package/dist/lib/watcher.js.map +1 -1
- package/dist/lib/workspace-token.d.ts +69 -0
- package/dist/lib/workspace-token.d.ts.map +1 -0
- package/dist/lib/workspace-token.js +93 -0
- package/dist/lib/workspace-token.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { CLI_VERSION } from "./version.js";
|
|
6
|
+
import { startTracker, trackerDiagnostics } from "./tracker.js";
|
|
7
|
+
/** The `engines.node` floor in package.json; pinned to it by test. */
|
|
8
|
+
export const NODE_MIN_MAJOR = 18;
|
|
9
|
+
export const REINSTALL_COMMAND = "npm install -g litmus-cli@latest";
|
|
10
|
+
/** How long the watcher smoke waits for the ready marker before calling the boot failed. */
|
|
11
|
+
export const WATCHER_BOOT_TIMEOUT_MS = 6000;
|
|
12
|
+
/** Registry document for the `latest` dist-tag. */
|
|
13
|
+
export const NPM_LATEST_URL = "https://registry.npmjs.org/litmus-cli/latest";
|
|
14
|
+
const REGISTRY_TIMEOUT_MS = 5000;
|
|
15
|
+
const SERVER_TIMEOUT_MS = 8000;
|
|
16
|
+
/** Above this the connection is reported as slow. Still `ok`: slowness is not a fix the candidate can apply. */
|
|
17
|
+
const SERVER_SLOW_MS = 3000;
|
|
18
|
+
// ── Node ─────────────────────────────────────────────────────────────
|
|
19
|
+
export function parseNodeMajor(version) {
|
|
20
|
+
const m = /^v?(\d+)/.exec(version.trim());
|
|
21
|
+
return m ? Number(m[1]) : null;
|
|
22
|
+
}
|
|
23
|
+
export function checkNodeVersion(version = process.version) {
|
|
24
|
+
const major = parseNodeMajor(version);
|
|
25
|
+
if (major === null) {
|
|
26
|
+
return { name: "node", status: "warn", detail: `Node.js: version could not be read (${version})` };
|
|
27
|
+
}
|
|
28
|
+
if (major < NODE_MIN_MAJOR) {
|
|
29
|
+
return {
|
|
30
|
+
name: "node",
|
|
31
|
+
status: "fail",
|
|
32
|
+
detail: `Node.js: ${version} is too old. Install Node.js ${NODE_MIN_MAJOR} or newer from https://nodejs.org, then run "litmus setup-check" again.`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return { name: "node", status: "ok", detail: `Node.js: ${version}` };
|
|
36
|
+
}
|
|
37
|
+
// ── CLI version against npm ──────────────────────────────────────────
|
|
38
|
+
/**
|
|
39
|
+
* Numeric compare of dotted versions; prerelease suffixes are ignored, so
|
|
40
|
+
* `1.4.25-beta` compares as `1.4.25`. Returns <0 when a is behind b.
|
|
41
|
+
*/
|
|
42
|
+
export function compareVersions(a, b) {
|
|
43
|
+
const parse = (v) => v.replace(/^v/, "").split("-")[0].split(".").map((n) => Number(n) || 0);
|
|
44
|
+
const pa = parse(a);
|
|
45
|
+
const pb = parse(b);
|
|
46
|
+
const len = Math.max(pa.length, pb.length);
|
|
47
|
+
for (let i = 0; i < len; i++) {
|
|
48
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
49
|
+
if (d !== 0)
|
|
50
|
+
return d;
|
|
51
|
+
}
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
export async function fetchNpmLatest(fetchImpl = fetch) {
|
|
55
|
+
const res = await fetchImpl(NPM_LATEST_URL, { signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS) });
|
|
56
|
+
if (!res.ok)
|
|
57
|
+
throw new Error(`registry answered HTTP ${res.status}`);
|
|
58
|
+
const body = (await res.json());
|
|
59
|
+
if (typeof body?.version !== "string")
|
|
60
|
+
throw new Error("registry document has no version");
|
|
61
|
+
return body.version;
|
|
62
|
+
}
|
|
63
|
+
export async function checkCliVersion(opts = {}) {
|
|
64
|
+
const installed = opts.installed ?? CLI_VERSION;
|
|
65
|
+
let latest;
|
|
66
|
+
try {
|
|
67
|
+
latest = await (opts.fetchLatest ?? fetchNpmLatest)();
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
// Could not verify is not a failure: the candidate's install may be
|
|
71
|
+
// current and the registry merely unreachable from where they sit.
|
|
72
|
+
return {
|
|
73
|
+
name: "cli_version",
|
|
74
|
+
status: "warn",
|
|
75
|
+
detail: `Litmus CLI: ${installed} installed (could not check npm for a newer version)`,
|
|
76
|
+
verbose: err instanceof Error ? err.message : String(err),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (installed === "unknown") {
|
|
80
|
+
return { name: "cli_version", status: "warn", detail: `Litmus CLI: installed version could not be read (${latest} is current)` };
|
|
81
|
+
}
|
|
82
|
+
if (compareVersions(installed, latest) < 0) {
|
|
83
|
+
return {
|
|
84
|
+
name: "cli_version",
|
|
85
|
+
status: "fail",
|
|
86
|
+
detail: `Litmus CLI: ${installed} installed, ${latest} is current. Run "${REINSTALL_COMMAND}" and then "litmus setup-check" again.`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { name: "cli_version", status: "ok", detail: `Litmus CLI: ${installed} (current)` };
|
|
90
|
+
}
|
|
91
|
+
// ── Server reachability ──────────────────────────────────────────────
|
|
92
|
+
/**
|
|
93
|
+
* Any HTTP answer from the API host counts as reachable — the question is
|
|
94
|
+
* whether this machine can talk to us at all, not whether the endpoint is
|
|
95
|
+
* healthy. `/api/health/db` is public (Clerk's middleware lets `/api/health/`
|
|
96
|
+
* through) and cheap.
|
|
97
|
+
*/
|
|
98
|
+
export async function checkServerReachable(opts) {
|
|
99
|
+
const now = opts.now ?? Date.now;
|
|
100
|
+
const url = `${opts.apiBase.replace(/\/$/, "")}/api/health/db`;
|
|
101
|
+
const started = now();
|
|
102
|
+
try {
|
|
103
|
+
const res = await (opts.fetchImpl ?? fetch)(url, { signal: AbortSignal.timeout(SERVER_TIMEOUT_MS) });
|
|
104
|
+
const ms = now() - started;
|
|
105
|
+
const slow = ms > SERVER_SLOW_MS;
|
|
106
|
+
return {
|
|
107
|
+
name: "server",
|
|
108
|
+
status: "ok",
|
|
109
|
+
detail: slow
|
|
110
|
+
? `Server connection: OK but slow (${ms}ms). Downloads may take a while; a wired or stronger connection helps.`
|
|
111
|
+
: `Server connection: OK (${ms}ms)`,
|
|
112
|
+
verbose: `GET ${url} -> HTTP ${res.status}`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
return {
|
|
117
|
+
name: "server",
|
|
118
|
+
status: "fail",
|
|
119
|
+
detail: `Server connection: FAILED. Check your internet connection (and any VPN or firewall), then run "litmus setup-check" again.`,
|
|
120
|
+
verbose: `GET ${url}: ${err instanceof Error ? err.message : String(err)}`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ── git ──────────────────────────────────────────────────────────────
|
|
125
|
+
export function checkGit(exec = (cmd) => execSync(cmd, { stdio: "pipe", windowsHide: true }).toString()) {
|
|
126
|
+
try {
|
|
127
|
+
const out = exec("git --version").trim();
|
|
128
|
+
return { name: "git", status: "ok", detail: `Git: ${out.replace(/^git version\s*/i, "")}` };
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
// `litmus init` runs `git init` and the tracker records commits, so a
|
|
132
|
+
// missing git is a real blocker with a one-step fix.
|
|
133
|
+
return {
|
|
134
|
+
name: "git",
|
|
135
|
+
status: "fail",
|
|
136
|
+
detail: "Git: not found. Install it from https://git-scm.com, then run \"litmus setup-check\" again.",
|
|
137
|
+
verbose: err instanceof Error ? err.message : String(err),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ── Watcher boot smoke ───────────────────────────────────────────────
|
|
142
|
+
/**
|
|
143
|
+
* Spawn the REAL watcher against a throwaway directory and wait for it to
|
|
144
|
+
* stamp `tracker.ready`, which it does only once its monitors are running
|
|
145
|
+
* (end of its module init). Then ask it to exit through the shutdown
|
|
146
|
+
* sentinel it already polls — never a signal at a pid — and delete the dir.
|
|
147
|
+
*
|
|
148
|
+
* Safe to run on a candidate's machine before any assessment exists: with no
|
|
149
|
+
* config the watcher has no token and no backend, so it uploads nothing, arms
|
|
150
|
+
* no auto-submit, and its capture nudge needs two scans ten minutes apart
|
|
151
|
+
* before it will notify. Everything it writes lands under the temp dir.
|
|
152
|
+
*/
|
|
153
|
+
export async function checkWatcherBoot(opts = {}) {
|
|
154
|
+
const timeoutMs = opts.timeoutMs ?? WATCHER_BOOT_TIMEOUT_MS;
|
|
155
|
+
let dir;
|
|
156
|
+
try {
|
|
157
|
+
dir = mkdtempSync(path.join(opts.tmpRoot ?? os.tmpdir(), "litmus-setup-check-"));
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
return {
|
|
161
|
+
name: "watcher",
|
|
162
|
+
status: "fail",
|
|
163
|
+
detail: "Activity tracker: could not create a temporary folder to test in. Check that your temp directory is writable, then run \"litmus setup-check\" again.",
|
|
164
|
+
verbose: err instanceof Error ? err.message : String(err),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const litmusDir = path.join(dir, ".litmus");
|
|
168
|
+
const readyFile = path.join(litmusDir, "tracker.ready");
|
|
169
|
+
const logFile = path.join(litmusDir, "tracker.log");
|
|
170
|
+
const stderrTail = () => {
|
|
171
|
+
try {
|
|
172
|
+
return readFileSync(logFile, "utf8").trim().split("\n").slice(-12).join("\n");
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return "(no tracker.log)";
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
let stopOutcome = null;
|
|
179
|
+
const annotate = (check) => {
|
|
180
|
+
if (!stopOutcome || stopOutcome.how === "sentinel" || stopOutcome.how === "never_started")
|
|
181
|
+
return check;
|
|
182
|
+
const note = stopOutcome.exited
|
|
183
|
+
? `smoke watcher ignored the shutdown sentinel and was stopped with ${stopOutcome.how.toUpperCase()}`
|
|
184
|
+
: `smoke watcher pid could not be stopped; its folder ${dir} was left in place`;
|
|
185
|
+
return { ...check, verbose: check.verbose ? `${check.verbose}\n${note}` : note };
|
|
186
|
+
};
|
|
187
|
+
let result;
|
|
188
|
+
try {
|
|
189
|
+
mkdirSync(litmusDir, { recursive: true });
|
|
190
|
+
startTracker(dir);
|
|
191
|
+
const deadline = Date.now() + timeoutMs;
|
|
192
|
+
let ready = false;
|
|
193
|
+
let died = false;
|
|
194
|
+
while (Date.now() < deadline) {
|
|
195
|
+
if (existsSync(readyFile)) {
|
|
196
|
+
ready = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
const diag = trackerDiagnostics(dir);
|
|
200
|
+
if (diag.pid !== null && !diag.pidAlive) {
|
|
201
|
+
died = true;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
205
|
+
}
|
|
206
|
+
if (ready) {
|
|
207
|
+
result = { name: "watcher", status: "ok", detail: "Activity tracker: starts and runs", verbose: stderrTail() };
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
const tail = stderrTail();
|
|
211
|
+
const looksBlocked = /EPERM|EACCES|operation not permitted|access is denied/i.test(tail);
|
|
212
|
+
result = {
|
|
213
|
+
name: "watcher",
|
|
214
|
+
status: "fail",
|
|
215
|
+
detail: died
|
|
216
|
+
? looksBlocked
|
|
217
|
+
? "Activity tracker: exited during startup because something on this machine blocked it from writing files. Antivirus or endpoint-protection software is the usual cause; allow Node.js and the folder you will work in, then run \"litmus setup-check\" again."
|
|
218
|
+
: "Activity tracker: exited during startup. Copy this whole output into an email to support@litmushiring.com."
|
|
219
|
+
: "Activity tracker: did not finish starting in time. Run \"litmus setup-check\" again; if this line repeats, copy this whole output into an email to support@litmushiring.com.",
|
|
220
|
+
verbose: tail,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
result = {
|
|
226
|
+
name: "watcher",
|
|
227
|
+
status: "fail",
|
|
228
|
+
detail: "Activity tracker: could not be started. Copy this whole output into an email to support@litmushiring.com.",
|
|
229
|
+
verbose: `${err instanceof Error ? err.message : String(err)}\n${stderrTail()}`,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
stopOutcome = await stopSmokeWatcher(dir);
|
|
234
|
+
}
|
|
235
|
+
return annotate(result);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Stop the smoke watcher and remove its directory, in that order and never
|
|
239
|
+
* the other way round (Greptile P1 on #2410): a watcher that wedged during
|
|
240
|
+
* init has not reached its sentinel poll and cannot see a shutdown file, so
|
|
241
|
+
* deleting the directory under it would orphan a live process with no
|
|
242
|
+
* control files left to stop it by.
|
|
243
|
+
*
|
|
244
|
+
* Escalation: the sentinel first (the watcher's own graceful path, and the
|
|
245
|
+
* only one that works on Windows without killing handlers), then SIGTERM,
|
|
246
|
+
* then SIGKILL. Signalling a pid is safe HERE where doctor refuses it: this
|
|
247
|
+
* pid was written by our own `startTracker` into a directory we created
|
|
248
|
+
* moments ago, so it is provably our child and not a recycled stranger.
|
|
249
|
+
* The directory is removed only once the process is gone; if it somehow
|
|
250
|
+
* survives even SIGKILL the directory is left in place so the files that
|
|
251
|
+
* name it are still there for a human.
|
|
252
|
+
*/
|
|
253
|
+
export async function stopSmokeWatcher(dir, opts = {}) {
|
|
254
|
+
const kill = opts.kill ?? ((pid, signal) => process.kill(pid, signal));
|
|
255
|
+
const graceMs = opts.graceMs ?? 3000;
|
|
256
|
+
const litmusDir = path.join(dir, ".litmus");
|
|
257
|
+
const alive = () => {
|
|
258
|
+
const diag = trackerDiagnostics(dir);
|
|
259
|
+
return diag.pid !== null && diag.pidAlive;
|
|
260
|
+
};
|
|
261
|
+
const waitForExit = async (ms) => {
|
|
262
|
+
const deadline = Date.now() + ms;
|
|
263
|
+
while (Date.now() < deadline) {
|
|
264
|
+
if (!alive())
|
|
265
|
+
return true;
|
|
266
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
267
|
+
}
|
|
268
|
+
return !alive();
|
|
269
|
+
};
|
|
270
|
+
const remove = () => {
|
|
271
|
+
try {
|
|
272
|
+
rmSync(dir, { recursive: true, force: true });
|
|
273
|
+
}
|
|
274
|
+
catch { /* a still-held handle on Windows; the OS temp cleaner takes it */ }
|
|
275
|
+
};
|
|
276
|
+
if (!alive()) {
|
|
277
|
+
remove();
|
|
278
|
+
return { exited: true, how: "never_started" };
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
writeFileSync(path.join(litmusDir, "shutdown"), "", "utf8");
|
|
282
|
+
}
|
|
283
|
+
catch { /* fall through to signals */ }
|
|
284
|
+
if (await waitForExit(graceMs)) {
|
|
285
|
+
remove();
|
|
286
|
+
return { exited: true, how: "sentinel" };
|
|
287
|
+
}
|
|
288
|
+
const pid = trackerDiagnostics(dir).pid;
|
|
289
|
+
if (pid !== null) {
|
|
290
|
+
try {
|
|
291
|
+
kill(pid, "SIGTERM");
|
|
292
|
+
}
|
|
293
|
+
catch { /* already gone */ }
|
|
294
|
+
if (await waitForExit(graceMs)) {
|
|
295
|
+
remove();
|
|
296
|
+
return { exited: true, how: "sigterm" };
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
kill(pid, "SIGKILL");
|
|
300
|
+
}
|
|
301
|
+
catch { /* already gone */ }
|
|
302
|
+
if (await waitForExit(graceMs)) {
|
|
303
|
+
remove();
|
|
304
|
+
return { exited: true, how: "sigkill" };
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return { exited: false, how: "survived" };
|
|
308
|
+
}
|
|
309
|
+
// ── Composition ──────────────────────────────────────────────────────
|
|
310
|
+
/**
|
|
311
|
+
* The checks a candidate can act on before init, in the order they print.
|
|
312
|
+
* `apiBase` is the resolved frontend base (`resolveApiBase`).
|
|
313
|
+
*/
|
|
314
|
+
export async function runEnvironmentChecks(opts) {
|
|
315
|
+
const results = [];
|
|
316
|
+
const push = (c) => { results.push(c); opts.record(c); };
|
|
317
|
+
push(checkNodeVersion());
|
|
318
|
+
push(await checkCliVersion());
|
|
319
|
+
push(checkGit());
|
|
320
|
+
push(await checkServerReachable({ apiBase: opts.apiBase }));
|
|
321
|
+
push(await checkWatcherBoot());
|
|
322
|
+
return results;
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=environment-checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment-checks.js","sourceRoot":"","sources":["../../src/lib/environment-checks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Cap on what we will upload, measured gzipped (base64 adds ~33% on top).
|
|
2
|
+
* A candidate who committed large binaries must not turn every five-minute
|
|
3
|
+
* tick into a multi-megabyte upload; their code still rides the files map. */
|
|
4
|
+
export declare const MAX_BUNDLE_BYTES: number;
|
|
5
|
+
export interface GitBundleDeps {
|
|
6
|
+
/** Seam for tests; defaults to a real `git` in `projectDir`. */
|
|
7
|
+
run?: (command: string, maxBuffer: number) => Buffer;
|
|
8
|
+
}
|
|
9
|
+
/** The commit HEAD points at, or null when this is not a repo or has no
|
|
10
|
+
* commits yet. Used to skip re-uploading a history that has not moved. */
|
|
11
|
+
export declare function currentHead(projectDir: string, deps?: GitBundleDeps): string | null;
|
|
12
|
+
/**
|
|
13
|
+
* Gzipped `git bundle --all`, or null when there is nothing worth sending: no
|
|
14
|
+
* repo, no commits, or a bundle over the cap. Every failure mode is null —
|
|
15
|
+
* history is a bonus on a snapshot whose real job is the code, so nothing here
|
|
16
|
+
* may cost the caller its upload.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildGitBundle(projectDir: string, deps?: GitBundleDeps): Buffer | null;
|
|
19
|
+
//# sourceMappingURL=git-bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-bundle.d.ts","sourceRoot":"","sources":["../../src/lib/git-bundle.ts"],"names":[],"mappings":"AAqBA;;+EAE+E;AAC/E,eAAO,MAAM,gBAAgB,QAAkB,CAAA;AAI/C,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;CACrD;AAgBD;2EAC2E;AAC3E,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,GAAG,IAAI,CAOvF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,aAAkB,GACvB,MAAM,GAAG,IAAI,CAgBf"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ENG-2238: the candidate's git history, in a form the periodic snapshot can
|
|
3
|
+
* carry.
|
|
4
|
+
*
|
|
5
|
+
* The snapshot's `files` map is text-only and drops every dotfile, so a
|
|
6
|
+
* server-side reconstruction (the deadline auto-submit, taken when this CLI is
|
|
7
|
+
* not running to submit for itself) rebuilt the candidate's code with no `.git`
|
|
8
|
+
* at all: their local commits were absent from their own submission, and the
|
|
9
|
+
* grader could only repeat the last ten commit messages as the candidate's
|
|
10
|
+
* claim rather than measure them.
|
|
11
|
+
*
|
|
12
|
+
* A bundle is git's own single-file transport: `git bundle create - --all`
|
|
13
|
+
* writes every commit and ref to stdout and the server clones it back into a
|
|
14
|
+
* real `.git`. It carries OBJECTS AND REFS ONLY — no `.git/config`, no hooks —
|
|
15
|
+
* so nothing candidate-controlled that git would later execute rides along,
|
|
16
|
+
* which is the hazard `backend/services/commit_diff.py` documents about reading
|
|
17
|
+
* somebody else's repository.
|
|
18
|
+
*/
|
|
19
|
+
import { execSync } from "child_process";
|
|
20
|
+
import { gzipSync } from "zlib";
|
|
21
|
+
/** Cap on what we will upload, measured gzipped (base64 adds ~33% on top).
|
|
22
|
+
* A candidate who committed large binaries must not turn every five-minute
|
|
23
|
+
* tick into a multi-megabyte upload; their code still rides the files map. */
|
|
24
|
+
export const MAX_BUNDLE_BYTES = 4 * 1024 * 1024;
|
|
25
|
+
/** Refuse to buffer a bundle bigger than this at all. */
|
|
26
|
+
const BUNDLE_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
27
|
+
function runner(projectDir, deps) {
|
|
28
|
+
return (deps.run ??
|
|
29
|
+
((command, maxBuffer) => execSync(command, {
|
|
30
|
+
cwd: projectDir,
|
|
31
|
+
maxBuffer,
|
|
32
|
+
timeout: 60000,
|
|
33
|
+
windowsHide: true,
|
|
34
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
35
|
+
})));
|
|
36
|
+
}
|
|
37
|
+
/** The commit HEAD points at, or null when this is not a repo or has no
|
|
38
|
+
* commits yet. Used to skip re-uploading a history that has not moved. */
|
|
39
|
+
export function currentHead(projectDir, deps = {}) {
|
|
40
|
+
try {
|
|
41
|
+
const out = runner(projectDir, deps)("git rev-parse HEAD", 10000).toString().trim();
|
|
42
|
+
return out || null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gzipped `git bundle --all`, or null when there is nothing worth sending: no
|
|
50
|
+
* repo, no commits, or a bundle over the cap. Every failure mode is null —
|
|
51
|
+
* history is a bonus on a snapshot whose real job is the code, so nothing here
|
|
52
|
+
* may cost the caller its upload.
|
|
53
|
+
*/
|
|
54
|
+
export function buildGitBundle(projectDir, deps = {}) {
|
|
55
|
+
let raw;
|
|
56
|
+
try {
|
|
57
|
+
raw = runner(projectDir, deps)("git bundle create - --all", BUNDLE_BUFFER_BYTES);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (!raw?.length)
|
|
63
|
+
return null;
|
|
64
|
+
const gz = gzipSync(raw);
|
|
65
|
+
if (gz.length > MAX_BUNDLE_BYTES) {
|
|
66
|
+
process.stderr.write(`[watcher] git bundle ${(gz.length / 1024 / 1024).toFixed(1)}MB over cap; skipping history upload\n`);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return gz;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=git-bundle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-bundle.js","sourceRoot":"","sources":["../../src/lib/git-bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAE/B;;+EAE+E;AAC/E,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AAC/C,yDAAyD;AACzD,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;AAO5C,SAAS,MAAM,CAAC,UAAkB,EAAE,IAAmB;IACrD,OAAO,CACL,IAAI,CAAC,GAAG;QACR,CAAC,CAAC,OAAe,EAAE,SAAiB,EAAE,EAAE,CACtC,QAAQ,CAAC,OAAO,EAAE;YAChB,GAAG,EAAE,UAAU;YACf,SAAS;YACT,OAAO,EAAE,KAAM;YACf,WAAW,EAAE,IAAI;YACjB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC,CACN,CAAA;AACH,CAAC;AAED;2EAC2E;AAC3E,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,OAAsB,EAAE;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,oBAAoB,EAAE,KAAM,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAA;QACpF,OAAO,GAAG,IAAI,IAAI,CAAA;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,UAAkB,EAClB,OAAsB,EAAE;IAExB,IAAI,GAAW,CAAA;IACf,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,2BAA2B,EAAE,mBAAmB,CAAC,CAAA;IAClF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,CAAC,GAAG,EAAE,MAAM;QAAE,OAAO,IAAI,CAAA;IAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;IACxB,IAAI,EAAE,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wBAAwB,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,wCAAwC,CACrG,CAAA;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What happens to a NATIVE editor window when the session ends — ENG-2154.
|
|
3
|
+
*
|
|
4
|
+
* ## The decision, which this ticket was asked to make explicitly
|
|
5
|
+
*
|
|
6
|
+
* **We do not close the candidate's editor window. We tell them what the window
|
|
7
|
+
* is doing instead.** Elena asked for the opposite ("when I finish submitting
|
|
8
|
+
* can you exit out of the ID editor, because the file tree is still there even
|
|
9
|
+
* though I'm disconnecting the codebase"), so the reasons are worth stating
|
|
10
|
+
* rather than leaving in a PR description.
|
|
11
|
+
*
|
|
12
|
+
* MEASURED FIRST, on a real VS Code 1.136 / Remote-SSH 0.128.0 window connected
|
|
13
|
+
* over ssh into a container whose transport was then killed. The window does not
|
|
14
|
+
* go quiet: the title bar and the Explorer root still read `[SSH: <host>]`, the
|
|
15
|
+
* status bar flips to `Disconnected from SSH: <host>`, and the resolver raises a
|
|
16
|
+
* modal reporting that it could not establish the connection and offering to retry.
|
|
17
|
+
* So the state a candidate is left in is not a silent stale file tree — it is an
|
|
18
|
+
* ERROR DIALOG, arriving seconds after they pressed submit. That is the ENG-2066
|
|
19
|
+
* misreading pointed at a different surface: a workspace that stops working with
|
|
20
|
+
* no explanation reads as a FAILED SUBMISSION. Disambiguating it is the whole
|
|
21
|
+
* job; making the window vanish is not.
|
|
22
|
+
*
|
|
23
|
+
* And closing it is not reachable anyway, for three independent reasons:
|
|
24
|
+
*
|
|
25
|
+
* 1. **Nothing of ours is running on the laptop at submit time.** `litmus
|
|
26
|
+
* connect` spawns the editor detached and returns; a v2 candidate then
|
|
27
|
+
* submits from the container (`litmus push` in the editor's own remote
|
|
28
|
+
* terminal) or from the assessment page. Closing the window would mean
|
|
29
|
+
* leaving a background daemon on a candidate's machine for the life of the
|
|
30
|
+
* assessment, purely to close a window.
|
|
31
|
+
* 2. **We could not honour "only the window we opened."** `code --remote` hands
|
|
32
|
+
* the request to an already-running Electron app and exits; the window is
|
|
33
|
+
* not a process we own. Every close mechanism available (osascript UI
|
|
34
|
+
* scripting, PowerShell `CloseMainWindow`, wmctrl) picks its target by
|
|
35
|
+
* window title, which is a guess that can land on a window the candidate
|
|
36
|
+
* opened themselves. The ticket forbids exactly that.
|
|
37
|
+
* 3. **macOS gates it behind a permission the candidate has not granted.** UI
|
|
38
|
+
* scripting needs an Automation/Accessibility grant; without it the attempt
|
|
39
|
+
* is refused (`-1743`), and the first attempt in a fresh grant state blocks
|
|
40
|
+
* on a system dialog — which is what ENG-1502 measured when it made its own
|
|
41
|
+
* close best-effort and detached.
|
|
42
|
+
*
|
|
43
|
+
* A window that vanishes while somebody is reading their own code is worse than
|
|
44
|
+
* one that says what happened, and (1)-(3) mean we could only ever have vanished
|
|
45
|
+
* it unreliably. So: say it, and leave the window to them.
|
|
46
|
+
*
|
|
47
|
+
* ## Why the copy lives in a module
|
|
48
|
+
*
|
|
49
|
+
* The same answer is needed at two moments, in two commands, and a second copy
|
|
50
|
+
* of it would rot — the argument `submit-guidance.ts` and the candidate page's
|
|
51
|
+
* `workspace-guidance.ts` already make:
|
|
52
|
+
*
|
|
53
|
+
* - `litmus connect` says it AHEAD of time, because it is the only laptop-side
|
|
54
|
+
* moment we have, and it is the only thing that reaches a candidate whose
|
|
55
|
+
* session ends some other way (they submit from the assessment page, the
|
|
56
|
+
* company ends the trial, the deadline cron fires).
|
|
57
|
+
* - `litmus push` says it AT the moment, in the terminal that is very often
|
|
58
|
+
* the disconnecting window's own.
|
|
59
|
+
*
|
|
60
|
+
* ## What the copy may claim
|
|
61
|
+
*
|
|
62
|
+
* NO EDITOR-SPECIFIC CONTROL IS NAMED. The dialog's buttons were measured on VS
|
|
63
|
+
* Code only; Cursor resolves `ssh-remote+` through `anysphere.remote-ssh`, a
|
|
64
|
+
* different extension whose dialog we have not measured, and naming a button a
|
|
65
|
+
* candidate cannot find costs us the trust they need in the rest of the sentence
|
|
66
|
+
* (the same line `workspace-guidance.ts` draws over its tool list). What is true
|
|
67
|
+
* of both is the shape: the window stays, it reports a lost connection, and
|
|
68
|
+
* closing it is theirs to do. That bound is tighter than it first looks and
|
|
69
|
+
* Greptile caught the first draft crossing it — describing WHAT THE EDITOR WILL
|
|
70
|
+
* ASK ("and offers to try again") is as unmeasured on Cursor as naming the
|
|
71
|
+
* button would be, and a candidate told to expect a prompt that never appears
|
|
72
|
+
* is left doubting the sentence that was meant to reassure them. Only the fact
|
|
73
|
+
* of a reported disconnection is verified on both.
|
|
74
|
+
*
|
|
75
|
+
* It also may not claim the assessment is FINISHED. A submit can still owe a
|
|
76
|
+
* post-submit walkthrough, and `runPush` prints the next step right after this;
|
|
77
|
+
* the claim here is only about the workspace and the window.
|
|
78
|
+
*/
|
|
79
|
+
/**
|
|
80
|
+
* The state the candidate's native editor window is now in, said once.
|
|
81
|
+
*
|
|
82
|
+
* Printed after a submit that ENDED the session — a fresh capture, an
|
|
83
|
+
* `already_submitted`, or a confirmed empty submission. Never after a refusal, a
|
|
84
|
+
* cancellation or a failure: in all three the workspace is still live and the
|
|
85
|
+
* window is still connected, so this would be a lie about both.
|
|
86
|
+
*
|
|
87
|
+
* Written as a conditional ("if ... is open") because `litmus push` runs from
|
|
88
|
+
* four places — a native remote window's terminal, the browser IDE's terminal, a
|
|
89
|
+
* plain ssh session, and a laptop with an explicit token — and cannot tell them
|
|
90
|
+
* apart. The browser IDE's own tab is deliberately not mentioned: ENG-2066 ends
|
|
91
|
+
* that one by navigating it, so describing it here would contradict what that
|
|
92
|
+
* candidate is about to see.
|
|
93
|
+
*/
|
|
94
|
+
export declare function editorEndingLines(): string[];
|
|
95
|
+
/**
|
|
96
|
+
* The same fact, said before it happens, on the native-editor path of
|
|
97
|
+
* `litmus connect`.
|
|
98
|
+
*
|
|
99
|
+
* Two lines, and deliberately no more. This is the ONLY route that reaches a
|
|
100
|
+
* candidate who ends their session from somewhere we do not run — the assessment
|
|
101
|
+
* page's Submit, End trial, the deadline cron — and for them it is the whole
|
|
102
|
+
* warning. But it is also printed at the top of a session, minutes or days
|
|
103
|
+
* before it is true, next to a Cursor notice that is already long; anything more
|
|
104
|
+
* than the fact and what to do with it is noise at the wrong moment.
|
|
105
|
+
*
|
|
106
|
+
* @param editorLabel what the candidate calls the editor being opened, from
|
|
107
|
+
* `NATIVE_EDITORS` — this one CAN be named, because `connect` knows which one
|
|
108
|
+
* it is launching. The dialog's controls still cannot be.
|
|
109
|
+
*/
|
|
110
|
+
export declare function editorEndingForewarning(editorLabel: string): string[];
|
|
111
|
+
//# sourceMappingURL=session-end.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-end.d.ts","sourceRoot":"","sources":["../../src/lib/session-end.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6EG;AAEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAS5C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAKrE"}
|