@ran-sh/dsh-crew 0.5.3 → 0.5.5

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.
@@ -1,115 +1,148 @@
1
- import {
2
- copyFileSync,
3
- existsSync,
4
- mkdirSync,
5
- readFileSync,
6
- rmSync,
7
- writeFileSync,
8
- } from 'node:fs';
9
- import { dirname, join } from 'node:path';
10
- import { homedir } from 'node:os';
11
-
12
- export const WINDOWS_STARTUP_FILENAME = 'DSH Crew.vbs';
13
- export const WINDOWS_LAUNCHER_FILENAME = 'start-dsh-crew.cmd';
14
- export const WINDOWS_HELPER_FILENAME = 'start-dsh-crew.ps1';
15
-
16
- function defaultStartupDir({ home, env }) {
17
- if (home === homedir() && env.APPDATA) {
18
- return join(env.APPDATA, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
19
- }
20
- return join(home, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
21
- }
22
-
23
- function paths({ home, startupDir, env }) {
24
- const launcherFile = join(home, '.config', 'dsh-crew', 'launchers', WINDOWS_LAUNCHER_FILENAME);
25
- const helperFile = join(home, '.config', 'dsh-crew', 'launchers', WINDOWS_HELPER_FILENAME);
26
- const resolvedStartupDir = startupDir ?? defaultStartupDir({ home, env });
27
- return {
28
- launcherFile,
29
- helperFile,
30
- startupFile: join(resolvedStartupDir, WINDOWS_STARTUP_FILENAME),
31
- };
32
- }
33
-
34
- function renderVbs(template, launcherFile) {
35
- const escaped = launcherFile.replace(/"/g, '""');
36
- return template.replace('__LAUNCHER__', escaped);
37
- }
38
-
39
- export function windowsStartupStatus({
40
- home = homedir(),
41
- startupDir,
42
- platform = process.platform,
43
- env = process.env,
44
- } = {}) {
45
- if (platform !== 'win32') return { supported: false, installed: false, ready: false };
46
- const resolved = paths({ home, startupDir, env });
47
- const installed = existsSync(resolved.startupFile)
48
- || existsSync(resolved.launcherFile)
49
- || existsSync(resolved.helperFile);
50
- let ready = existsSync(resolved.startupFile)
51
- && existsSync(resolved.launcherFile)
52
- && existsSync(resolved.helperFile);
53
- if (ready) {
54
- try {
55
- const startup = readFileSync(resolved.startupFile, 'utf16le').replace(/^\uFEFF/, '');
56
- const launcher = readFileSync(resolved.launcherFile, 'utf8');
57
- const helper = readFileSync(resolved.helperFile, 'utf8');
58
- ready = startup.includes(resolved.launcherFile)
59
- && startup.includes('--watch')
60
- && launcher.includes(WINDOWS_HELPER_FILENAME)
61
- && helper.includes('DSH Crew managed Windows launcher')
62
- && helper.includes('DSHCrewServiceSupervisor');
63
- } catch { ready = false; }
64
- }
65
- return { supported: true, installed, ready, ...resolved };
66
- }
67
-
68
- export function installWindowsStartup({
69
- home = homedir(),
70
- root,
71
- startupDir,
72
- platform = process.platform,
73
- env = process.env,
74
- } = {}) {
75
- if (platform !== 'win32') return { ok: true, supported: false, changed: false };
76
- if (!root) return { ok: false, supported: true, code: 'STARTUP_SOURCE_REQUIRED' };
77
- const sourceLauncher = join(root, 'windows', WINDOWS_LAUNCHER_FILENAME);
78
- const sourceHelper = join(root, 'windows', WINDOWS_HELPER_FILENAME);
79
- const sourceVbs = join(root, 'windows', 'start-dsh-crew.vbs');
80
- if (!existsSync(sourceLauncher) || !existsSync(sourceHelper) || !existsSync(sourceVbs)) {
81
- return { ok: false, supported: true, code: 'STARTUP_ASSET_MISSING' };
82
- }
83
- const resolved = paths({ home, startupDir, env });
84
- mkdirSync(dirname(resolved.launcherFile), { recursive: true });
85
- mkdirSync(dirname(resolved.startupFile), { recursive: true });
86
- const beforeLauncher = existsSync(resolved.launcherFile) ? readFileSync(resolved.launcherFile) : null;
87
- const beforeHelper = existsSync(resolved.helperFile) ? readFileSync(resolved.helperFile) : null;
88
- const beforeStartup = existsSync(resolved.startupFile) ? readFileSync(resolved.startupFile) : null;
89
- copyFileSync(sourceLauncher, resolved.launcherFile);
90
- copyFileSync(sourceHelper, resolved.helperFile);
91
- const rendered = renderVbs(readFileSync(sourceVbs, 'utf8'), resolved.launcherFile);
92
- writeFileSync(resolved.startupFile, `\uFEFF${rendered}`, 'utf16le');
93
- const changed = !beforeLauncher?.equals(readFileSync(resolved.launcherFile))
94
- || !beforeHelper?.equals(readFileSync(resolved.helperFile))
95
- || !beforeStartup?.equals(readFileSync(resolved.startupFile));
96
- return { ok: true, supported: true, changed, ...resolved };
97
- }
98
-
99
- export function uninstallWindowsStartup({
100
- home = homedir(),
101
- startupDir,
102
- platform = process.platform,
103
- env = process.env,
104
- } = {}) {
105
- if (platform !== 'win32') return { ok: true, supported: false, removed: false };
106
- const resolved = paths({ home, startupDir, env });
107
- let removed = false;
108
- for (const file of [resolved.startupFile, resolved.launcherFile, resolved.helperFile]) {
109
- if (existsSync(file)) {
110
- rmSync(file, { force: true });
111
- removed = true;
112
- }
113
- }
114
- return { ok: true, supported: true, removed, ...resolved };
115
- }
1
+ import {
2
+ copyFileSync,
3
+ existsSync,
4
+ mkdirSync,
5
+ readFileSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from 'node:fs';
9
+ import { dirname, join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+
12
+ export const WINDOWS_STARTUP_FILENAME = 'DSH Crew.vbs';
13
+ export const WINDOWS_LAUNCHER_FILENAME = 'start-dsh-crew.cmd';
14
+ export const WINDOWS_HELPER_FILENAME = 'start-dsh-crew.ps1';
15
+
16
+ function defaultStartupDir({ home, env }) {
17
+ if (home === homedir() && env.APPDATA) {
18
+ return join(env.APPDATA, 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
19
+ }
20
+ return join(home, 'AppData', 'Roaming', 'Microsoft', 'Windows', 'Start Menu', 'Programs', 'Startup');
21
+ }
22
+
23
+ function paths({ home, startupDir, env }) {
24
+ const launcherFile = join(home, '.config', 'dsh-crew', 'launchers', WINDOWS_LAUNCHER_FILENAME);
25
+ const helperFile = join(home, '.config', 'dsh-crew', 'launchers', WINDOWS_HELPER_FILENAME);
26
+ const resolvedStartupDir = startupDir ?? defaultStartupDir({ home, env });
27
+ return {
28
+ launcherFile,
29
+ helperFile,
30
+ startupFile: join(resolvedStartupDir, WINDOWS_STARTUP_FILENAME),
31
+ };
32
+ }
33
+
34
+ function renderVbs(template, launcherFile) {
35
+ const escaped = launcherFile.replace(/"/g, '""');
36
+ return template.replace('__LAUNCHER__', escaped);
37
+ }
38
+
39
+ function isManagedStartupFile(file, resolved) {
40
+ try {
41
+ if (file === resolved.startupFile) {
42
+ const text = readFileSync(file, 'utf16le').replace(/^\uFEFF/, '');
43
+ return text.includes(resolved.launcherFile) && (/WScript\.Shell/i.test(text) || /--watch/i.test(text) || /--background/i.test(text));
44
+ }
45
+ if (file === resolved.launcherFile) {
46
+ const text = readFileSync(file, 'utf8');
47
+ return (/DSH Crew Launcher/i.test(text) || /start-dsh-crew\.ps1/i.test(text)) && /dsh-crew/i.test(text);
48
+ }
49
+ if (file === resolved.helperFile) {
50
+ const text = readFileSync(file, 'utf8');
51
+ return /DSH Crew managed Windows launcher/i.test(text) || /DSHCrewServiceSupervisor/i.test(text);
52
+ }
53
+ } catch {
54
+ return false;
55
+ }
56
+ return false;
57
+ }
58
+
59
+ function startupTargetConflicts(resolved) {
60
+ return [resolved.startupFile, resolved.launcherFile, resolved.helperFile]
61
+ .filter((file) => existsSync(file) && !isManagedStartupFile(file, resolved));
62
+ }
63
+
64
+ export function windowsStartupStatus({
65
+ home = homedir(),
66
+ startupDir,
67
+ platform = process.platform,
68
+ env = process.env,
69
+ } = {}) {
70
+ if (platform !== 'win32') return { supported: false, installed: false, ready: false };
71
+ const resolved = paths({ home, startupDir, env });
72
+ const installed = existsSync(resolved.startupFile)
73
+ || existsSync(resolved.launcherFile)
74
+ || existsSync(resolved.helperFile);
75
+ let ready = existsSync(resolved.startupFile)
76
+ && existsSync(resolved.launcherFile)
77
+ && existsSync(resolved.helperFile);
78
+ if (ready) {
79
+ try {
80
+ const startup = readFileSync(resolved.startupFile, 'utf16le').replace(/^\uFEFF/, '');
81
+ const launcher = readFileSync(resolved.launcherFile, 'utf8');
82
+ const helper = readFileSync(resolved.helperFile, 'utf8');
83
+ ready = startup.includes(resolved.launcherFile)
84
+ && startup.includes('--watch')
85
+ && launcher.includes(WINDOWS_HELPER_FILENAME)
86
+ && helper.includes('DSH Crew managed Windows launcher')
87
+ && helper.includes('DSHCrewServiceSupervisor');
88
+ } catch { ready = false; }
89
+ }
90
+ return { supported: true, installed, ready, ...resolved };
91
+ }
92
+
93
+ export function installWindowsStartup({
94
+ home = homedir(),
95
+ root,
96
+ startupDir,
97
+ platform = process.platform,
98
+ env = process.env,
99
+ } = {}) {
100
+ if (platform !== 'win32') return { ok: true, supported: false, changed: false };
101
+ if (!root) return { ok: false, supported: true, code: 'STARTUP_SOURCE_REQUIRED' };
102
+ const sourceLauncher = join(root, 'windows', WINDOWS_LAUNCHER_FILENAME);
103
+ const sourceHelper = join(root, 'windows', WINDOWS_HELPER_FILENAME);
104
+ const sourceVbs = join(root, 'windows', 'start-dsh-crew.vbs');
105
+ if (!existsSync(sourceLauncher) || !existsSync(sourceHelper) || !existsSync(sourceVbs)) {
106
+ return { ok: false, supported: true, code: 'STARTUP_ASSET_MISSING' };
107
+ }
108
+ const resolved = paths({ home, startupDir, env });
109
+ const conflicts = startupTargetConflicts(resolved);
110
+ if (conflicts.length > 0) {
111
+ return { ok: false, supported: true, code: 'STARTUP_TARGET_COLLISION', conflicts, ...resolved };
112
+ }
113
+ mkdirSync(dirname(resolved.launcherFile), { recursive: true });
114
+ mkdirSync(dirname(resolved.startupFile), { recursive: true });
115
+ const beforeLauncher = existsSync(resolved.launcherFile) ? readFileSync(resolved.launcherFile) : null;
116
+ const beforeHelper = existsSync(resolved.helperFile) ? readFileSync(resolved.helperFile) : null;
117
+ const beforeStartup = existsSync(resolved.startupFile) ? readFileSync(resolved.startupFile) : null;
118
+ copyFileSync(sourceLauncher, resolved.launcherFile);
119
+ copyFileSync(sourceHelper, resolved.helperFile);
120
+ const rendered = renderVbs(readFileSync(sourceVbs, 'utf8'), resolved.launcherFile);
121
+ writeFileSync(resolved.startupFile, `\uFEFF${rendered}`, 'utf16le');
122
+ const changed = !beforeLauncher?.equals(readFileSync(resolved.launcherFile))
123
+ || !beforeHelper?.equals(readFileSync(resolved.helperFile))
124
+ || !beforeStartup?.equals(readFileSync(resolved.startupFile));
125
+ return { ok: true, supported: true, changed, ...resolved };
126
+ }
127
+
128
+ export function uninstallWindowsStartup({
129
+ home = homedir(),
130
+ startupDir,
131
+ platform = process.platform,
132
+ env = process.env,
133
+ } = {}) {
134
+ if (platform !== 'win32') return { ok: true, supported: false, removed: false };
135
+ const resolved = paths({ home, startupDir, env });
136
+ let removed = false;
137
+ const preserved = [];
138
+ for (const file of [resolved.startupFile, resolved.launcherFile, resolved.helperFile]) {
139
+ if (!existsSync(file)) continue;
140
+ if (!isManagedStartupFile(file, resolved)) {
141
+ preserved.push(file);
142
+ continue;
143
+ }
144
+ rmSync(file, { force: true });
145
+ removed = true;
146
+ }
147
+ return { ok: true, supported: true, removed, preserved, ...resolved };
148
+ }