erosolar-cli 1.7.13 → 1.7.15

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 (65) hide show
  1. package/dist/core/responseVerifier.d.ts +79 -0
  2. package/dist/core/responseVerifier.d.ts.map +1 -0
  3. package/dist/core/responseVerifier.js +443 -0
  4. package/dist/core/responseVerifier.js.map +1 -0
  5. package/dist/shell/interactiveShell.d.ts +5 -0
  6. package/dist/shell/interactiveShell.d.ts.map +1 -1
  7. package/dist/shell/interactiveShell.js +45 -14
  8. package/dist/shell/interactiveShell.js.map +1 -1
  9. package/dist/ui/ShellUIAdapter.d.ts +3 -0
  10. package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
  11. package/dist/ui/ShellUIAdapter.js +4 -10
  12. package/dist/ui/ShellUIAdapter.js.map +1 -1
  13. package/dist/ui/display.d.ts +15 -0
  14. package/dist/ui/display.d.ts.map +1 -1
  15. package/dist/ui/display.js +57 -0
  16. package/dist/ui/display.js.map +1 -1
  17. package/dist/ui/persistentPrompt.d.ts +4 -0
  18. package/dist/ui/persistentPrompt.d.ts.map +1 -1
  19. package/dist/ui/persistentPrompt.js +10 -11
  20. package/dist/ui/persistentPrompt.js.map +1 -1
  21. package/package.json +1 -1
  22. package/dist/bin/core/agent.js +0 -362
  23. package/dist/bin/core/agentProfileManifest.js +0 -187
  24. package/dist/bin/core/agentProfiles.js +0 -34
  25. package/dist/bin/core/agentRulebook.js +0 -135
  26. package/dist/bin/core/agentSchemaLoader.js +0 -233
  27. package/dist/bin/core/contextManager.js +0 -412
  28. package/dist/bin/core/contextWindow.js +0 -122
  29. package/dist/bin/core/customCommands.js +0 -80
  30. package/dist/bin/core/errors/apiKeyErrors.js +0 -114
  31. package/dist/bin/core/errors/errorTypes.js +0 -340
  32. package/dist/bin/core/errors/safetyValidator.js +0 -304
  33. package/dist/bin/core/errors.js +0 -32
  34. package/dist/bin/core/modelDiscovery.js +0 -755
  35. package/dist/bin/core/preferences.js +0 -224
  36. package/dist/bin/core/schemaValidator.js +0 -92
  37. package/dist/bin/core/secretStore.js +0 -199
  38. package/dist/bin/core/sessionStore.js +0 -187
  39. package/dist/bin/core/toolRuntime.js +0 -290
  40. package/dist/bin/core/types.js +0 -1
  41. package/dist/bin/shell/bracketedPasteManager.js +0 -350
  42. package/dist/bin/shell/fileChangeTracker.js +0 -65
  43. package/dist/bin/shell/interactiveShell.js +0 -2908
  44. package/dist/bin/shell/liveStatus.js +0 -78
  45. package/dist/bin/shell/shellApp.js +0 -290
  46. package/dist/bin/shell/systemPrompt.js +0 -60
  47. package/dist/bin/shell/updateManager.js +0 -108
  48. package/dist/bin/ui/ShellUIAdapter.js +0 -459
  49. package/dist/bin/ui/UnifiedUIController.js +0 -183
  50. package/dist/bin/ui/animation/AnimationScheduler.js +0 -430
  51. package/dist/bin/ui/codeHighlighter.js +0 -854
  52. package/dist/bin/ui/designSystem.js +0 -121
  53. package/dist/bin/ui/display.js +0 -1222
  54. package/dist/bin/ui/interrupts/InterruptManager.js +0 -437
  55. package/dist/bin/ui/layout.js +0 -139
  56. package/dist/bin/ui/orchestration/StatusOrchestrator.js +0 -403
  57. package/dist/bin/ui/outputMode.js +0 -38
  58. package/dist/bin/ui/persistentPrompt.js +0 -183
  59. package/dist/bin/ui/richText.js +0 -338
  60. package/dist/bin/ui/shortcutsHelp.js +0 -87
  61. package/dist/bin/ui/telemetry/UITelemetry.js +0 -443
  62. package/dist/bin/ui/textHighlighter.js +0 -210
  63. package/dist/bin/ui/theme.js +0 -116
  64. package/dist/bin/ui/toolDisplay.js +0 -423
  65. package/dist/bin/ui/toolDisplayAdapter.js +0 -357
@@ -1,78 +0,0 @@
1
- export class LiveStatusTracker {
2
- constructor() {
3
- this.base = null;
4
- this.overrides = new Map();
5
- this.overrideOrder = [];
6
- this.listeners = new Set();
7
- }
8
- subscribe(listener) {
9
- this.listeners.add(listener);
10
- listener(this.currentState());
11
- return () => {
12
- this.listeners.delete(listener);
13
- };
14
- }
15
- setBase(text, options = {}) {
16
- if (!text?.trim()) {
17
- this.base = null;
18
- this.emit();
19
- return;
20
- }
21
- this.base = this.buildState(text, options);
22
- this.emit();
23
- }
24
- pushOverride(id, text, options = {}) {
25
- if (!id?.trim() || !text?.trim()) {
26
- return;
27
- }
28
- this.overrides.set(id, this.buildState(text, options));
29
- if (!this.overrideOrder.includes(id)) {
30
- this.overrideOrder.push(id);
31
- }
32
- this.emit();
33
- }
34
- clearOverride(id) {
35
- if (!id?.trim()) {
36
- return;
37
- }
38
- this.overrides.delete(id);
39
- const index = this.overrideOrder.indexOf(id);
40
- if (index >= 0) {
41
- this.overrideOrder.splice(index, 1);
42
- }
43
- this.emit();
44
- }
45
- clearOverrides() {
46
- this.overrides.clear();
47
- this.overrideOrder.length = 0;
48
- this.emit();
49
- }
50
- reset() {
51
- this.base = null;
52
- this.clearOverrides();
53
- }
54
- currentState() {
55
- for (let index = this.overrideOrder.length - 1; index >= 0; index -= 1) {
56
- const id = this.overrideOrder[index];
57
- const state = this.overrides.get(id);
58
- if (state) {
59
- return state;
60
- }
61
- }
62
- return this.base;
63
- }
64
- buildState(text, options) {
65
- return {
66
- text: text.trim(),
67
- detail: options.detail?.trim() || undefined,
68
- tone: options.tone,
69
- startedAt: Date.now(),
70
- };
71
- }
72
- emit() {
73
- const state = this.currentState();
74
- for (const listener of this.listeners) {
75
- listener(state);
76
- }
77
- }
78
- }
@@ -1,290 +0,0 @@
1
- import { exit } from 'node:process';
2
- import { readFileSync } from 'node:fs';
3
- import { dirname, resolve } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
- import '../config.js';
6
- import { buildWorkspaceContext, resolveWorkspaceCaptureOptions } from '../workspace.js';
7
- import { InteractiveShell } from './interactiveShell.js';
8
- import { display } from '../ui/display.js';
9
- import { loadActiveProfilePreference, loadModelPreference, loadToolSettings, } from '../core/preferences.js';
10
- import { createNodeRuntime } from '../runtime/node.js';
11
- import { buildEnabledToolSet, evaluateToolPermissions, isPluginEnabled, } from '../capabilities/toolRegistry.js';
12
- import { listAgentProfiles, hasAgentProfile } from '../core/agentProfiles.js';
13
- import { maybeOfferCliUpdate } from './updateManager.js';
14
- import { LiveStatusTracker } from './liveStatus.js';
15
- import { buildInteractiveSystemPrompt } from './systemPrompt.js';
16
- import { ShellUIAdapter } from '../ui/ShellUIAdapter.js';
17
- import { stdout } from 'node:process';
18
- import { setPlainOutputMode } from '../ui/outputMode.js';
19
- import { getPluginIdsFromFlags, loadPlugin } from '../plugins/index.js';
20
- import { quickCheckProviders } from '../core/modelDiscovery.js';
21
- /**
22
- * Launch the interactive shell with full capability awareness for the selected profile.
23
- */
24
- export async function launchShell(defaultProfile, options = {}) {
25
- try {
26
- const { profileOverride, promptArgs, sessionRestore, plainOutput, pluginFlags } = parseLaunchArguments(process.argv.slice(2));
27
- if (plainOutput) {
28
- setPlainOutputMode(true);
29
- }
30
- // Load enabled plugins based on CLI flags
31
- const enabledPluginIds = getPluginIdsFromFlags(pluginFlags);
32
- const envProfileOverride = process.env['EROSOLAR_PROFILE']?.trim() || null;
33
- const allowProfileSelection = Boolean(options.enableProfileSelection);
34
- const availableProfiles = listAgentProfiles();
35
- const rawSavedProfile = allowProfileSelection ? loadActiveProfilePreference() : null;
36
- const savedProfile = rawSavedProfile && hasAgentProfile(rawSavedProfile) ? rawSavedProfile : null;
37
- const profile = resolveLaunchProfile({
38
- defaultProfile,
39
- availableProfiles,
40
- cliOverride: profileOverride,
41
- envOverride: envProfileOverride,
42
- savedProfile,
43
- allowSavedProfile: allowProfileSelection,
44
- });
45
- const workingDir = process.cwd();
46
- const workspaceOptions = resolveWorkspaceCaptureOptions(process.env);
47
- const workspaceContext = buildWorkspaceContext(workingDir, workspaceOptions);
48
- const statusTracker = new LiveStatusTracker();
49
- // Create unified UI adapter early to get the tool observer
50
- // We'll add the file change callback after creating the shell
51
- const uiAdapter = new ShellUIAdapter(stdout, display, {
52
- useUnifiedUI: true,
53
- preserveCompatibility: true,
54
- enableTelemetry: true,
55
- debugMode: false,
56
- });
57
- // Use the unified UI adapter's tool observer instead of the legacy one
58
- const toolObserver = uiAdapter.createToolObserver();
59
- const toolSettings = loadToolSettings();
60
- const toolSelection = buildEnabledToolSet(toolSettings);
61
- const permissionSummary = evaluateToolPermissions(toolSelection);
62
- const pluginFilter = (plugin) => isPluginEnabled(plugin.id, permissionSummary.allowedPluginIds);
63
- const runtime = await createNodeRuntime({
64
- profile,
65
- workspaceContext,
66
- workingDir,
67
- toolObserver,
68
- adapterOptions: {
69
- filter: pluginFilter,
70
- },
71
- });
72
- const session = runtime.session;
73
- const profileConfig = session.profileConfig;
74
- const providerTools = session.toolRuntime.listProviderTools();
75
- const persistedSelection = profileConfig.modelLocked || profileConfig.providerLocked
76
- ? null
77
- : loadModelPreference(profile);
78
- const initialModel = persistedSelection ?? {
79
- provider: profileConfig.provider,
80
- model: profileConfig.model,
81
- temperature: profileConfig.temperature,
82
- maxTokens: profileConfig.maxTokens,
83
- };
84
- const enhancedSystemPrompt = buildInteractiveSystemPrompt(profileConfig.systemPrompt, profileConfig.label, providerTools);
85
- const version = readPackageVersion();
86
- display.showWelcome(profileConfig.label, profile, initialModel.model, initialModel.provider, workingDir, version);
87
- display.showAvailableTools(providerTools);
88
- // Show available providers and models on launch
89
- try {
90
- const providerStatus = await quickCheckProviders();
91
- display.showProvidersStatus(providerStatus);
92
- }
93
- catch {
94
- // Silently ignore provider check errors
95
- }
96
- const continueLaunch = await maybeOfferCliUpdate(version);
97
- if (!continueLaunch) {
98
- return;
99
- }
100
- if (permissionSummary.warnings.length) {
101
- reportSkippedTools(permissionSummary.warnings);
102
- }
103
- const agentSelection = allowProfileSelection
104
- ? {
105
- defaultProfile,
106
- persistedProfile: savedProfile,
107
- options: availableProfiles,
108
- }
109
- : undefined;
110
- // Load optional plugins if enabled via CLI flags
111
- for (const pluginId of enabledPluginIds) {
112
- await loadPlugin(pluginId);
113
- }
114
- const shell = new InteractiveShell({
115
- profile,
116
- profileLabel: profileConfig.label,
117
- workingDir,
118
- session,
119
- baseSystemPrompt: enhancedSystemPrompt,
120
- initialModel,
121
- agentSelection,
122
- statusTracker,
123
- uiAdapter,
124
- workspaceOptions,
125
- sessionRestore,
126
- enabledPlugins: enabledPluginIds,
127
- });
128
- const initialPrompt = promptArgs.join(' ').trim();
129
- await shell.start(initialPrompt || undefined);
130
- }
131
- catch (error) {
132
- const message = error instanceof Error ? error.message : String(error);
133
- display.showError(message);
134
- exit(1);
135
- }
136
- }
137
- function parseLaunchArguments(argv) {
138
- const promptArgs = [];
139
- let override = null;
140
- let plainOutput = false;
141
- let sessionRestore = undefined;
142
- const pluginFlags = {};
143
- const expectValue = (flag, value) => {
144
- if (value && value.trim()) {
145
- return value.trim();
146
- }
147
- throw new Error(`Missing value for ${flag}.`);
148
- };
149
- for (let index = 0; index < argv.length; index += 1) {
150
- const token = argv[index];
151
- if (!token) {
152
- continue;
153
- }
154
- if (token === '--profile' || token === '-p') {
155
- const value = expectValue(token, argv[index + 1]);
156
- override = value;
157
- index += 1;
158
- continue;
159
- }
160
- if (token.startsWith('--profile=')) {
161
- const [, candidate] = token.split('=');
162
- if (!candidate?.trim()) {
163
- throw new Error('Missing value for --profile.');
164
- }
165
- override = candidate.trim();
166
- continue;
167
- }
168
- if (token === '--plain' || token === '--plain-output') {
169
- plainOutput = true;
170
- continue;
171
- }
172
- if (token === '--restore-session') {
173
- sessionRestore = { mode: 'autosave' };
174
- continue;
175
- }
176
- if (token === '--session' || token === '-s') {
177
- const value = expectValue(token, argv[index + 1]);
178
- sessionRestore = { mode: 'session-id', sessionId: value };
179
- index += 1;
180
- continue;
181
- }
182
- if (token.startsWith('--session=')) {
183
- const [, candidate] = token.split('=');
184
- if (!candidate?.trim()) {
185
- throw new Error('Missing value for --session.');
186
- }
187
- sessionRestore = { mode: 'session-id', sessionId: candidate.trim() };
188
- continue;
189
- }
190
- // Plugin flags
191
- if (token === '--alpha-zero') {
192
- pluginFlags.alphaZero = true;
193
- continue;
194
- }
195
- if (token === '--no-alpha-zero') {
196
- pluginFlags.alphaZero = false;
197
- continue;
198
- }
199
- if (token === '--coding') {
200
- pluginFlags.coding = true;
201
- continue;
202
- }
203
- if (token === '--no-coding') {
204
- pluginFlags.coding = false;
205
- continue;
206
- }
207
- if (token === '--security') {
208
- pluginFlags.security = true;
209
- continue;
210
- }
211
- if (token === '--no-security') {
212
- pluginFlags.security = false;
213
- continue;
214
- }
215
- if (token === '--all-plugins') {
216
- pluginFlags.allPlugins = true;
217
- continue;
218
- }
219
- promptArgs.push(token);
220
- }
221
- return {
222
- profileOverride: override,
223
- promptArgs,
224
- plainOutput,
225
- sessionRestore,
226
- pluginFlags,
227
- };
228
- }
229
- function resolveLaunchProfile(input) {
230
- if (input.cliOverride) {
231
- const resolved = matchProfile(input.cliOverride, input.availableProfiles);
232
- if (!resolved) {
233
- throw new Error(`Unknown agent profile "${input.cliOverride}". Run "/agents" to view available options.`);
234
- }
235
- return resolved;
236
- }
237
- if (input.envOverride?.trim()) {
238
- const resolved = matchProfile(input.envOverride, input.availableProfiles);
239
- if (!resolved) {
240
- throw new Error(`Unknown agent profile "${input.envOverride}" provided via EROSOLAR_PROFILE.`);
241
- }
242
- return resolved;
243
- }
244
- if (input.allowSavedProfile) {
245
- const saved = matchProfile(input.savedProfile, input.availableProfiles);
246
- if (saved) {
247
- return saved;
248
- }
249
- }
250
- const fallback = matchProfile(input.defaultProfile, input.availableProfiles);
251
- if (fallback) {
252
- return fallback;
253
- }
254
- throw new Error('No registered CLI profile is available.');
255
- }
256
- function matchProfile(candidate, availableProfiles) {
257
- if (!candidate) {
258
- return null;
259
- }
260
- const trimmed = candidate.trim();
261
- if (!trimmed) {
262
- return null;
263
- }
264
- if (hasAgentProfile(trimmed)) {
265
- return trimmed;
266
- }
267
- const lower = trimmed.toLowerCase();
268
- const match = availableProfiles.find((profile) => profile.name.toLowerCase() === lower);
269
- return match ? match.name : null;
270
- }
271
- function reportSkippedTools(entries) {
272
- for (const warning of entries) {
273
- const details = warning.reason === 'missing-secret' && warning.secretId
274
- ? `missing ${warning.secretId}`
275
- : warning.reason;
276
- const suffix = warning.secretId ? ' (use /secrets to configure it)' : '';
277
- display.showWarning(`Skipped ${warning.label} — ${details}${suffix}`);
278
- }
279
- }
280
- function readPackageVersion() {
281
- try {
282
- const filePath = fileURLToPath(import.meta.url);
283
- const packagePath = resolve(dirname(filePath), '../../package.json');
284
- const payload = JSON.parse(readFileSync(packagePath, 'utf8'));
285
- return typeof payload.version === 'string' ? payload.version : '0.0.0';
286
- }
287
- catch {
288
- return '0.0.0';
289
- }
290
- }
@@ -1,60 +0,0 @@
1
- export function buildInteractiveSystemPrompt(basePrompt, profileLabel, tools) {
2
- const name = profileLabel || 'Active Agent';
3
- const toolSummary = formatToolSummary(tools, { maxDescriptionLength: 80 });
4
- const capabilityLines = [
5
- '✓ Full file system read/write access',
6
- '✓ Bash command execution (including git commands)',
7
- '✓ Git operations: status, diff, add, commit, push, branch management',
8
- '✓ Advanced code search and analysis',
9
- '✓ Workspace snapshot is guaranteed and immutable',
10
- '✓ Tool usage is narrated to the operator in real time',
11
- ];
12
- const behaviorGuidelines = [
13
- 'Narrate intent before using tools',
14
- 'Follow rulebook guardrails; cite rule IDs when referencing',
15
- 'Use README, package.json, and workspace context as evidence',
16
- 'Read before editing; re-read after to confirm',
17
- 'Keep responses concise; reference actual commands/files',
18
- 'Request missing info explicitly',
19
- 'For self-improvement tasks: make changes, commit frequently, and push when complete',
20
- ];
21
- const behaviorSection = behaviorGuidelines
22
- .map((line, index) => `${index + 1}. ${line}`)
23
- .join('\n');
24
- return `${basePrompt}
25
-
26
- You are ${name}, running in an interactive shell with full capabilities:
27
-
28
- AVAILABLE TOOLS:
29
- ${toolSummary}
30
-
31
- CAPABILITIES:
32
- ${capabilityLines.join('\n')}
33
-
34
- BEHAVIOR GUIDELINES:
35
- ${behaviorSection}
36
-
37
- Remember: answer truthfully, ground everything in the workspace, and let the logs show what you actually did.`;
38
- }
39
- function formatToolSummary(tools, options = {}) {
40
- if (!tools.length) {
41
- return '- (no tools are registered in this session)';
42
- }
43
- return tools
44
- .map((tool) => {
45
- const description = tool.description ? sanitizeWhitespace(tool.description) : 'No description provided.';
46
- const summary = truncate(description, options.maxDescriptionLength);
47
- return `- ${tool.name}: ${summary}`;
48
- })
49
- .join('\n');
50
- }
51
- function sanitizeWhitespace(value) {
52
- return value.replace(/\s+/g, ' ').trim();
53
- }
54
- function truncate(value, maxLength) {
55
- if (!maxLength || value.length <= maxLength) {
56
- return value;
57
- }
58
- const safeLength = Math.max(0, maxLength - 3);
59
- return `${value.slice(0, safeLength).trimEnd()}...`;
60
- }
@@ -1,108 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { stdin as input, stdout as output } from 'node:process';
3
- import { createInterface } from 'node:readline/promises';
4
- import { display } from '../ui/display.js';
5
- const PACKAGE_NAME = 'erosolar-cli';
6
- const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
7
- const FETCH_TIMEOUT_MS = 4000;
8
- export async function maybeOfferCliUpdate(currentVersion) {
9
- try {
10
- const latestVersion = await fetchLatestVersion();
11
- if (!latestVersion) {
12
- return true;
13
- }
14
- if (!isNewerVersion(latestVersion, currentVersion)) {
15
- return true;
16
- }
17
- display.showInfo([
18
- `A new Erosolar CLI release is available.`,
19
- `Current version: ${currentVersion}`,
20
- `Latest version: ${latestVersion}`,
21
- ].join('\n'));
22
- if (!input.isTTY || !output.isTTY) {
23
- display.showInfo(`Run "npm install -g ${PACKAGE_NAME}@latest" when you're ready to upgrade.`);
24
- return true;
25
- }
26
- const shouldUpdate = await promptForUpdate();
27
- if (!shouldUpdate) {
28
- display.showInfo(`Continuing with ${currentVersion}. You can upgrade later via "npm install -g ${PACKAGE_NAME}@latest".`);
29
- return true;
30
- }
31
- const success = await installLatestVersion();
32
- if (success) {
33
- display.showInfo(`Update complete. Relaunch the CLI to start using ${PACKAGE_NAME}@${latestVersion}.`);
34
- return false;
35
- }
36
- display.showWarning(`Failed to install ${PACKAGE_NAME}@latest. Please run "npm install -g ${PACKAGE_NAME}@latest" manually.`);
37
- return true;
38
- }
39
- catch {
40
- return true;
41
- }
42
- }
43
- async function fetchLatestVersion() {
44
- const controller = new AbortController();
45
- const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
46
- timeout.unref?.();
47
- try {
48
- const response = await fetch(REGISTRY_URL, {
49
- headers: { 'user-agent': `${PACKAGE_NAME}/update-check` },
50
- signal: controller.signal,
51
- });
52
- if (!response.ok) {
53
- return null;
54
- }
55
- const payload = (await response.json());
56
- return typeof payload.version === 'string' ? payload.version : null;
57
- }
58
- catch {
59
- return null;
60
- }
61
- finally {
62
- clearTimeout(timeout);
63
- }
64
- }
65
- function isNewerVersion(latest, current) {
66
- const latestParts = normalizeVersion(latest);
67
- const currentParts = normalizeVersion(current);
68
- const length = Math.max(latestParts.length, currentParts.length);
69
- for (let index = 0; index < length; index += 1) {
70
- const nextLatest = latestParts[index] ?? 0;
71
- const nextCurrent = currentParts[index] ?? 0;
72
- if (nextLatest > nextCurrent) {
73
- return true;
74
- }
75
- if (nextLatest < nextCurrent) {
76
- return false;
77
- }
78
- }
79
- return false;
80
- }
81
- function normalizeVersion(value) {
82
- const sanitized = value.split('-')[0] ?? '';
83
- return sanitized
84
- .split('.')
85
- .map((segment) => Number.parseInt(segment, 10))
86
- .filter((segment) => Number.isFinite(segment) && segment >= 0);
87
- }
88
- async function promptForUpdate() {
89
- const prompt = createInterface({ input, output });
90
- try {
91
- const answer = await prompt.question('Update now? (Y/n): ');
92
- const normalized = answer.trim().toLowerCase();
93
- return normalized === '' || normalized === 'y' || normalized === 'yes';
94
- }
95
- finally {
96
- prompt.close();
97
- }
98
- }
99
- async function installLatestVersion() {
100
- const binary = process.platform === 'win32' ? 'npm.cmd' : 'npm';
101
- return new Promise((resolve) => {
102
- const child = spawn(binary, ['install', '-g', `${PACKAGE_NAME}@latest`], {
103
- stdio: 'inherit',
104
- });
105
- child.on('error', () => resolve(false));
106
- child.on('close', (code) => resolve(code === 0));
107
- });
108
- }