reslop 0.2.7 → 0.3.1

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 (158) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +16 -14
  3. package/bin/reslop.js +8 -5
  4. package/lib/agents/agents.js +573 -0
  5. package/lib/agents/allow.js +71 -0
  6. package/lib/agents/allow.json +17 -0
  7. package/lib/agents/models.js +467 -0
  8. package/lib/api.js +15 -12
  9. package/lib/cli.js +43 -68
  10. package/lib/{files.js → common/files.js} +48 -55
  11. package/lib/{find.js → common/find.js} +1 -1
  12. package/lib/common/format.js +101 -0
  13. package/lib/{utilities.js → common/process.js} +7 -95
  14. package/lib/common/tasks.json +27 -0
  15. package/lib/common/utilities.js +112 -0
  16. package/lib/dashboard/git.js +9 -16
  17. package/lib/dashboard/model.js +90 -49
  18. package/lib/dashboard/npm.js +17 -13
  19. package/lib/dashboard/tree.js +27 -30
  20. package/lib/{session → dashboard}/watch.js +36 -31
  21. package/lib/{workspace.js → dashboard/workspace.js} +23 -38
  22. package/lib/{deps.js → deps/deps.js} +31 -45
  23. package/lib/{deps-diff.js → deps/diff.js} +35 -25
  24. package/lib/{manifest.js → deps/manifest.js} +47 -30
  25. package/lib/diff/align.js +1 -2
  26. package/lib/diff/diff.js +2 -2
  27. package/lib/diff/display.js +29 -5
  28. package/lib/diff/inline.js +10 -14
  29. package/lib/diff/items.js +6 -4
  30. package/lib/diff/patch.js +39 -3
  31. package/lib/diff/unit.js +45 -16
  32. package/lib/{git-branches.js → git/branches.js} +53 -48
  33. package/lib/{git-deps.js → git/deps.js} +151 -95
  34. package/lib/git/git.js +71 -0
  35. package/lib/git/worktree.js +504 -0
  36. package/lib/highlight/bash.js +1 -2
  37. package/lib/highlight/csv.js +1 -2
  38. package/lib/highlight/dot.js +1 -2
  39. package/lib/{highlight.js → highlight/highlight.js} +42 -11
  40. package/lib/highlight/html.js +20 -18
  41. package/lib/highlight/json.js +1 -2
  42. package/lib/highlight/log.js +1 -2
  43. package/lib/highlight/txt.js +1 -2
  44. package/lib/{session → input}/actions.js +114 -48
  45. package/lib/{editor.js → input/editor.js} +16 -8
  46. package/lib/{keys.js → input/keys.js} +24 -45
  47. package/lib/input/line-edit.js +78 -0
  48. package/lib/input/menu-pick.js +70 -0
  49. package/lib/render/agents.js +642 -0
  50. package/lib/render/branches.js +4 -5
  51. package/lib/render/commits.js +54 -64
  52. package/lib/render/{dash-activity.js → dashboard/activity.js} +25 -40
  53. package/lib/render/{dash-blocks.js → dashboard/blocks.js} +131 -137
  54. package/lib/render/{dashboard.js → dashboard/dashboard.js} +25 -22
  55. package/lib/render/diff.js +166 -45
  56. package/lib/render/field.js +131 -0
  57. package/lib/render/files.js +9 -14
  58. package/lib/render/footer.js +385 -0
  59. package/lib/render/frame.js +123 -25
  60. package/lib/render/header.js +176 -0
  61. package/lib/render/log.js +320 -0
  62. package/lib/render/menu.js +60 -0
  63. package/lib/render/notes.js +16 -11
  64. package/lib/render/npm.js +322 -196
  65. package/lib/render/packages.js +9 -13
  66. package/lib/render/primitives.js +101 -15
  67. package/lib/render/render.js +7 -11
  68. package/lib/render/repos.js +13 -25
  69. package/lib/render/status.js +370 -0
  70. package/lib/render/status.json +85 -0
  71. package/lib/render/{dash-table.js → table.js} +13 -60
  72. package/lib/render/tiles.js +109 -48
  73. package/lib/render/unit.js +32 -13
  74. package/lib/{report-model.js → report/model.js} +4 -6
  75. package/lib/{report-parse.js → report/parse.js} +9 -14
  76. package/lib/{report-render.js → report/render.js} +48 -19
  77. package/lib/{report-run.js → report/run.js} +42 -30
  78. package/lib/{report-test.js → report/test.js} +56 -55
  79. package/lib/{report-tools.js → report/tools.js} +24 -33
  80. package/lib/review/agent-instructions.md +14 -0
  81. package/lib/review/format.js +420 -0
  82. package/lib/{review-merge.js → review/merge.js} +9 -8
  83. package/lib/review/review.js +190 -0
  84. package/lib/review/store.js +281 -0
  85. package/lib/review/templates.js +83 -0
  86. package/lib/runs/commands.js +14 -0
  87. package/lib/runs/logs.js +299 -0
  88. package/lib/runs/output.js +174 -0
  89. package/lib/runs/process.js +113 -0
  90. package/lib/{runs.js → runs/runs.js} +84 -36
  91. package/lib/runs/scripts.js +150 -0
  92. package/lib/session/agents/agents.js +976 -0
  93. package/lib/session/agents/job.js +320 -0
  94. package/lib/session/agents/jobs.js +355 -0
  95. package/lib/session/agents/options.js +153 -0
  96. package/lib/session/agents/plans.js +83 -0
  97. package/lib/session/branches.js +33 -40
  98. package/lib/session/change-actions.js +34 -100
  99. package/lib/session/commit.js +91 -52
  100. package/lib/session/compose.js +189 -147
  101. package/lib/session/dashboard.js +23 -22
  102. package/lib/session/dispatch.js +86 -21
  103. package/lib/session/files-pane.js +47 -26
  104. package/lib/session/find.js +16 -29
  105. package/lib/session/import.js +93 -0
  106. package/lib/session/items.js +8 -17
  107. package/lib/session/lifecycle.js +78 -72
  108. package/lib/session/load.js +2 -4
  109. package/lib/session/navigation.js +6 -3
  110. package/lib/session/npm/history.js +250 -0
  111. package/lib/session/npm/npm.js +836 -0
  112. package/lib/session/{packages.js → npm/packages.js} +21 -14
  113. package/lib/session/ops.js +125 -34
  114. package/lib/session/pointer.js +202 -10
  115. package/lib/session/prefs.js +112 -0
  116. package/lib/session/reads.js +109 -0
  117. package/lib/session/review.js +28 -10
  118. package/lib/session/run-panel.js +40 -0
  119. package/lib/{session.js → session/session.js} +158 -63
  120. package/lib/session/tasks.js +321 -66
  121. package/lib/session/templates.js +1 -1
  122. package/lib/session/terminal.js +94 -12
  123. package/lib/session/unit.js +35 -30
  124. package/lib/session/update.js +194 -6
  125. package/lib/session/view.js +65 -65
  126. package/lib/session/workspace.js +39 -78
  127. package/lib/{capabilities.js → source/capabilities.js} +3 -12
  128. package/lib/{github.js → source/github.js} +102 -32
  129. package/lib/{gitlab.js → source/gitlab.js} +87 -10
  130. package/lib/{remote.js → source/remote.js} +144 -40
  131. package/lib/source/review-threads.graphql +20 -0
  132. package/lib/source/source.js +74 -0
  133. package/lib/term/ansi.js +396 -0
  134. package/lib/{clipboard.js → term/clipboard.js} +3 -6
  135. package/lib/{select.js → term/select.js} +4 -3
  136. package/lib/term/themes/dark.json +122 -0
  137. package/lib/term/themes/light.json +122 -0
  138. package/lib/term/wide.txt +21 -0
  139. package/lib/{wrap.js → term/wrap.js} +4 -4
  140. package/package.json +2 -2
  141. package/lib/ansi.js +0 -545
  142. package/lib/detect.js +0 -34
  143. package/lib/diff/edit.js +0 -33
  144. package/lib/git-worktree.js +0 -455
  145. package/lib/git.js +0 -79
  146. package/lib/npm-commands.js +0 -522
  147. package/lib/render/chrome.js +0 -806
  148. package/lib/render/present.js +0 -19
  149. package/lib/render/report.js +0 -43
  150. package/lib/render/search.js +0 -25
  151. package/lib/review.js +0 -745
  152. package/lib/session/accessors.js +0 -26
  153. package/lib/session/edit-click.js +0 -57
  154. package/lib/session/npm.js +0 -452
  155. package/lib/session/progress.js +0 -41
  156. package/lib/source.js +0 -36
  157. package/lib/update.js +0 -192
  158. /package/lib/{git-reword-todo.js → git/reword-todo.js} +0 -0
@@ -1,522 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const childProcess = require('node:child_process');
6
- const { spawn } = childProcess;
7
- const metautil = require('metautil');
8
- const { jsonParse } = metautil;
9
- const ansi = require('./ansi.js');
10
- const { stripAnsi, visibleWidth } = ansi;
11
- const utilities = require('./utilities.js');
12
- const { npmBin, npmOpts, spawnBase, unquote, dateStamp } = utilities;
13
-
14
- const MANIFEST = 'package.json';
15
- const SECTIONS = ['dependencies', 'devDependencies', 'optionalDependencies'];
16
-
17
- const readText = (file) => {
18
- try {
19
- return fs.readFileSync(file, 'utf8');
20
- } catch {
21
- return '';
22
- }
23
- };
24
-
25
- const readManifest = (root) => {
26
- const text = readText(path.join(root, MANIFEST));
27
- if (!text) return null;
28
- const pkg = jsonParse(text);
29
- if (!pkg || typeof pkg !== 'object') return null;
30
- return pkg;
31
- };
32
-
33
- const writeManifest = (root, pkg) => {
34
- const file = path.join(root, MANIFEST);
35
- fs.writeFileSync(file, `${JSON.stringify(pkg, null, 2)}\n`);
36
- };
37
-
38
- const scriptEntries = (pkg) => {
39
- const scripts = pkg.scripts;
40
- if (!scripts || typeof scripts !== 'object') return [];
41
- const entries = [];
42
- for (const name of Object.keys(scripts)) {
43
- const command = scripts[name];
44
- if (typeof command !== 'string') continue;
45
- entries.push({ name, command, kind: 'script' });
46
- }
47
- return entries;
48
- };
49
-
50
- const binNames = (pkg) => {
51
- if (!pkg || typeof pkg !== 'object') return [];
52
- const bin = pkg.bin;
53
- if (typeof bin === 'string' && bin) {
54
- const raw = `${pkg.name ?? ''}`;
55
- const base = raw.startsWith('@') ? raw.split('/')[1] : raw;
56
- return base ? [base] : [];
57
- }
58
- if (!bin || typeof bin !== 'object' || Array.isArray(bin)) return [];
59
- const isPath = (name) => typeof bin[name] === 'string' && bin[name] !== '';
60
- return Object.keys(bin).filter(isPath);
61
- };
62
-
63
- const declaredNames = (pkg) => {
64
- const names = [];
65
- for (const section of SECTIONS) {
66
- const map = pkg[section];
67
- if (!map || typeof map !== 'object') continue;
68
- names.push(...Object.keys(map));
69
- }
70
- return names;
71
- };
72
-
73
- const depManifest = (root, name) => {
74
- const parts = name.startsWith('@') ? name.split('/').slice(0, 2) : [name];
75
- return path.join(root, 'node_modules', ...parts, MANIFEST);
76
- };
77
-
78
- const binEntries = (root, pkg, taken) => {
79
- const entries = [];
80
- const seen = new Set(taken);
81
- for (const name of declaredNames(pkg)) {
82
- const dep = jsonParse(readText(depManifest(root, name)));
83
- for (const bin of binNames(dep)) {
84
- if (seen.has(bin)) continue;
85
- seen.add(bin);
86
- entries.push({ name: bin, command: bin, kind: 'bin', package: name });
87
- }
88
- }
89
- entries.sort((left, right) => left.name.localeCompare(right.name));
90
- return entries;
91
- };
92
-
93
- const listCommands = (root) => {
94
- const pkg = readManifest(root);
95
- if (!pkg) return [];
96
- const scripts = scriptEntries(pkg);
97
- const taken = scripts.map((entry) => entry.name);
98
- return [...scripts, ...binEntries(root, pkg, taken)];
99
- };
100
-
101
- const isPassingTest = (line) => {
102
- const plain = stripAnsi(line);
103
- const text = plain.trim();
104
- if (!text) return false;
105
- if (/^\s*[✔✓√] /.test(plain)) return true;
106
- if (/^ok \d+ /.test(text)) return true;
107
- return /^\s*PASS\b/.test(plain);
108
- };
109
-
110
- const indentStack = (line) => {
111
- if (!/^\s*at\s/.test(stripAnsi(line))) return line;
112
- return ` ${line.trimStart()}`;
113
- };
114
-
115
- const withoutNodeFrame = (line) => {
116
- const plain = stripAnsi(line);
117
- if (!plain.includes('(node:')) return line;
118
- return /\{\s*$/.test(plain) ? '{' : null;
119
- };
120
-
121
- const withSep = (dir) => {
122
- const sep = dir.includes('\\') ? '\\' : '/';
123
- if (dir.endsWith(sep)) return dir;
124
- return `${dir}${sep}`;
125
- };
126
-
127
- const stripOne = (text, dir) => {
128
- if (!dir) return text;
129
- let next = text.split(withSep(dir)).join('');
130
- if (next.includes(dir)) next = next.split(dir).join('.');
131
- return next;
132
- };
133
-
134
- const relativize = (text, root) => {
135
- if (!root) return text;
136
- const slash = root.replaceAll('\\', '/');
137
- const back = slash.replaceAll('/', '\\');
138
- const resolved = path.resolve(root).replaceAll('\\', '/');
139
- const resolvedBack = resolved.replaceAll('/', '\\');
140
- let next = text;
141
- for (const dir of [slash, back, resolved, resolvedBack]) {
142
- next = stripOne(next, dir);
143
- }
144
- return next;
145
- };
146
-
147
- const traceLine = (line, root) => {
148
- const next = withoutNodeFrame(line);
149
- if (next === null) return null;
150
- return relativize(indentStack(next), root);
151
- };
152
-
153
- const objectEnd = (lines, start) => {
154
- if (stripAnsi(lines[start]).trim() !== '{') return -1;
155
- for (let i = start + 1; i < lines.length; i++) {
156
- if (stripAnsi(lines[i]).trim() === '}') return i;
157
- }
158
- return -1;
159
- };
160
-
161
- const parseInspect = (lines) => {
162
- const entries = [];
163
- for (const line of lines) {
164
- const plain = stripAnsi(line).trim().replace(/,$/, '');
165
- const match = /^([A-Za-z_][\w]*)\s*:\s*([\s\S]*)$/.exec(plain);
166
- if (!match) {
167
- if (!entries.length) return null;
168
- const last = entries[entries.length - 1];
169
- last[1] = `${last[1]}\n${plain}`;
170
- continue;
171
- }
172
- entries.push([match[1], unquote(match[2])]);
173
- }
174
- return entries.length ? entries : null;
175
- };
176
-
177
- const showValue = (value) => {
178
- if (value === null) return 'null';
179
- if (typeof value === 'string') return value;
180
- if (typeof value === 'number' || typeof value === 'boolean') {
181
- return `${value}`;
182
- }
183
- return JSON.stringify(value);
184
- };
185
-
186
- const parseObject = (lines) => {
187
- try {
188
- const value = JSON.parse(lines.join('\n'));
189
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
190
- return null;
191
- }
192
- const entries = Object.keys(value).map((key) => [
193
- key,
194
- showValue(value[key]),
195
- ]);
196
- return entries.length ? entries : null;
197
- } catch {
198
- return parseInspect(lines.slice(1, -1));
199
- }
200
- };
201
-
202
- const TABLE_KEY = '\x1b[900m';
203
- const TABLE_VAL = '\x1b[901m';
204
-
205
- const formatTable = (entries) => {
206
- const prepared = [];
207
- let keyWidth = 0;
208
- for (const entry of entries) {
209
- const lines = `${entry[1]}`.split('\n').filter((line) => line.trim());
210
- if (!lines.length) continue;
211
- prepared.push([entry[0], lines]);
212
- keyWidth = Math.max(keyWidth, visibleWidth(entry[0]));
213
- }
214
- const rows = [];
215
- for (const entry of prepared) {
216
- const lines = entry[1];
217
- for (let i = 0; i < lines.length; i++) {
218
- const name = i === 0 ? entry[0] : '';
219
- const pad = ' '.repeat(Math.max(0, keyWidth - visibleWidth(name)));
220
- const key = ` ${pad}${name} `;
221
- const value = ` ${lines[i]} `;
222
- rows.push(`${TABLE_KEY}${key}${TABLE_VAL} ${value}`);
223
- }
224
- }
225
- return rows;
226
- };
227
-
228
- const foldObjects = (lines) => {
229
- const out = [];
230
- for (let i = 0; i < lines.length; i++) {
231
- const end = objectEnd(lines, i);
232
- const entries = end < 0 ? null : parseObject(lines.slice(i, end + 1));
233
- if (!entries) {
234
- out.push(lines[i]);
235
- continue;
236
- }
237
- if (out.length && out[out.length - 1] !== '') out.push('');
238
- out.push(...formatTable(entries));
239
- i = end;
240
- }
241
- return out;
242
- };
243
-
244
- const reduceOutput = (text, root, status) => {
245
- const kept = [];
246
- for (const line of `${text ?? ''}`.split(/\r?\n/)) {
247
- if (isPassingTest(line)) continue;
248
- const next = traceLine(line, root);
249
- if (next !== null) kept.push(next);
250
- }
251
- const folded = foldObjects(kept);
252
- while (folded.length && folded[folded.length - 1] === '') folded.pop();
253
- if (status !== '') folded.push(`exit ${status ?? 'null'}`);
254
- if (!folded.length) return '';
255
- return `${folded.join('\n')}\n`;
256
- };
257
-
258
- const logFileName = (command, date, index) => {
259
- const safe = `${command}`.replace(/[^A-Za-z0-9._-]+/g, '-');
260
- const n = `${index}`.padStart(2, '0');
261
- return `${date}-${safe}-${n}.log`;
262
- };
263
-
264
- const LOG_DIR = '.log';
265
- const LOG_STALE_DAYS = 5;
266
- const DAY_MS = 24 * 60 * 60 * 1000;
267
- const LOG_DATE = /^(\d{4})-(\d{2})-(\d{2})-/;
268
-
269
- const logDay = (name) => {
270
- const match = LOG_DATE.exec(name);
271
- if (!match) return null;
272
- const year = Number(match[1]);
273
- const month = Number(match[2]);
274
- const day = Number(match[3]);
275
- if (month < 1 || month > 12 || day < 1 || day > 31) return null;
276
- return Date.UTC(year, month - 1, day) / DAY_MS;
277
- };
278
-
279
- const todayIndex = (now) =>
280
- Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) / DAY_MS;
281
-
282
- const isStaleLog = (name, now) => {
283
- const day = logDay(name);
284
- if (day === null) return false;
285
- return todayIndex(now) - day > LOG_STALE_DAYS;
286
- };
287
-
288
- const readLogNames = (dir) => {
289
- try {
290
- return fs.readdirSync(dir);
291
- } catch (error) {
292
- if (error.code === 'ENOENT') return [];
293
- throw error;
294
- }
295
- };
296
-
297
- const staleLogFiles = (root, now = new Date()) => {
298
- const dir = path.join(root, LOG_DIR);
299
- const files = [];
300
- let bytes = 0;
301
- for (const name of readLogNames(dir)) {
302
- if (!name.endsWith('.log') || !isStaleLog(name, now)) continue;
303
- const full = path.join(dir, name);
304
- let stat;
305
- try {
306
- stat = fs.statSync(full);
307
- } catch (error) {
308
- if (error.code === 'ENOENT') continue;
309
- throw error;
310
- }
311
- if (!stat.isFile()) continue;
312
- bytes += stat.size;
313
- files.push(full);
314
- }
315
- return { bytes, files };
316
- };
317
-
318
- const removeStaleLogs = (root, now = new Date()) => {
319
- const found = staleLogFiles(root, now);
320
- for (const full of found.files) fs.rmSync(full, { force: true });
321
- return found.bytes;
322
- };
323
-
324
- const logSlot = (dir, name) => {
325
- const rawName = name.replace(/\.log$/, '.raw.log');
326
- const full = path.join(dir, name);
327
- const rawFull = path.join(dir, rawName);
328
- return { name, full, dir, rawName, rawFull };
329
- };
330
-
331
- const nextLogFile = (root, command, now = new Date()) => {
332
- const date = dateStamp(now);
333
- const dir = path.join(root, LOG_DIR);
334
- let slot = null;
335
- for (let index = 1; index <= 100; index++) {
336
- slot = logSlot(dir, logFileName(command, date, index));
337
- if (!fs.existsSync(slot.full) && !fs.existsSync(slot.rawFull)) break;
338
- }
339
- return slot;
340
- };
341
-
342
- const logText = (text) =>
343
- stripAnsi(text)
344
- .split('\n')
345
- .map((line) => line.trimEnd())
346
- .join('\n');
347
-
348
- const saveLogs = async (slot, reduced, raw) => {
349
- await fs.promises.mkdir(slot.dir, { recursive: true });
350
- await Promise.all([
351
- fs.promises.writeFile(slot.full, logText(reduced)),
352
- fs.promises.writeFile(slot.rawFull, logText(raw)),
353
- ]);
354
- };
355
-
356
- const orderedScripts = (pkg) => {
357
- const scripts = pkg.scripts;
358
- if (!scripts || typeof scripts !== 'object' || Array.isArray(scripts)) {
359
- return {};
360
- }
361
- return scripts;
362
- };
363
-
364
- const pickScripts = (scripts, names) => {
365
- const ordered = {};
366
- for (const key of names) ordered[key] = scripts[key];
367
- return ordered;
368
- };
369
-
370
- const upsertScript = (pkg, prev, next) => {
371
- const scripts = orderedScripts(pkg);
372
- const names = Object.keys(scripts);
373
- if (!prev || !names.includes(prev)) {
374
- pkg.scripts = { ...scripts, [next.name]: next.command };
375
- return;
376
- }
377
- const ordered = {};
378
- for (const key of names) {
379
- if (key === prev) ordered[next.name] = next.command;
380
- else if (key !== next.name) ordered[key] = scripts[key];
381
- }
382
- pkg.scripts = ordered;
383
- };
384
-
385
- const saveScript = (root, prev, next) => {
386
- const pkg = readManifest(root) ?? {};
387
- upsertScript(pkg, prev, next);
388
- writeManifest(root, pkg);
389
- };
390
-
391
- const removeScript = (root, name) => {
392
- const pkg = readManifest(root);
393
- if (!pkg) return false;
394
- const scripts = orderedScripts(pkg);
395
- if (!Object.hasOwn(scripts, name)) return false;
396
- const names = Object.keys(scripts).filter((key) => key !== name);
397
- pkg.scripts = pickScripts(scripts, names);
398
- writeManifest(root, pkg);
399
- return true;
400
- };
401
-
402
- const reorderScript = (root, name, delta) => {
403
- const pkg = readManifest(root);
404
- if (!pkg) return false;
405
- const scripts = orderedScripts(pkg);
406
- const names = Object.keys(scripts);
407
- const at = names.indexOf(name);
408
- const next = at + delta;
409
- if (at < 0 || next < 0 || next >= names.length) return false;
410
- names[at] = names[next];
411
- names[next] = name;
412
- pkg.scripts = pickScripts(scripts, names);
413
- writeManifest(root, pkg);
414
- return true;
415
- };
416
-
417
- const signalPid = (pid, signal) => {
418
- try {
419
- process.kill(pid, signal);
420
- return true;
421
- } catch {
422
- return false;
423
- }
424
- };
425
-
426
- const killTree = (proc) => {
427
- const pid = proc.pid;
428
- if (!pid) return;
429
- if (process.platform === 'win32') {
430
- const killer = spawn(
431
- 'taskkill',
432
- ['/pid', String(pid), '/t', '/f'],
433
- spawnBase({ stdio: 'ignore' }),
434
- );
435
- killer.on('error', () => proc.kill());
436
- return;
437
- }
438
- const group = -pid;
439
- if (!signalPid(group, 'SIGTERM')) signalPid(pid, 'SIGTERM');
440
- const timer = setTimeout(() => {
441
- if (!signalPid(group, 'SIGKILL')) signalPid(pid, 'SIGKILL');
442
- }, 200);
443
- timer.unref();
444
- proc.once('close', () => clearTimeout(timer));
445
- };
446
-
447
- const commandEnv = (base = process.env) => {
448
- const env = { ...base };
449
- if (!env.NO_COLOR && !env.FORCE_COLOR) env.FORCE_COLOR = '1';
450
- if (!env.TERM) env.TERM = 'xterm-256color';
451
- env.RESLOP_OUTPUT = 'raw';
452
- return env;
453
- };
454
-
455
- const startNpm = (cwd, entry, onData, onClose) => {
456
- let raw = '';
457
- let settled = false;
458
- let stopped = false;
459
- const finish = (status) => {
460
- if (settled) return;
461
- settled = true;
462
- onClose({ status, text: raw });
463
- };
464
- const isBin = entry.kind === 'bin';
465
- const args = isBin ? ['exec', '--', entry.name] : ['run', entry.name];
466
- let child;
467
- try {
468
- const opts = npmOpts({
469
- cwd,
470
- env: commandEnv(),
471
- detached: process.platform !== 'win32',
472
- });
473
- child = spawn(npmBin(), args, opts);
474
- } catch (error) {
475
- raw = `${error.message}\n`;
476
- finish(1);
477
- return { kill() {} };
478
- }
479
- const push = (chunk) => {
480
- if (stopped || settled) return;
481
- raw += chunk;
482
- onData(raw);
483
- };
484
- for (const stream of [child.stdout, child.stderr]) {
485
- if (!stream) continue;
486
- stream.setEncoding('utf8');
487
- stream.on('data', push);
488
- }
489
- child.on('error', (error) => {
490
- if (!stopped) raw += `${error.message}\n`;
491
- finish(1);
492
- });
493
- child.on('close', (status) => finish(status));
494
- return {
495
- kill() {
496
- if (stopped || settled) return;
497
- stopped = true;
498
- killTree(child);
499
- },
500
- };
501
- };
502
-
503
- module.exports = {
504
- listCommands,
505
- reduceOutput,
506
- logFileName,
507
- nextLogFile,
508
- saveLogs,
509
- staleLogFiles,
510
- removeStaleLogs,
511
- saveScript,
512
- removeScript,
513
- reorderScript,
514
- startNpm,
515
- commandEnv,
516
- isPassingTest,
517
- traceLine,
518
- relativize,
519
- TABLE_KEY,
520
- TABLE_VAL,
521
- formatTable,
522
- };