agenshield 0.1.0

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 (184) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +201 -0
  3. package/README.md +91 -0
  4. package/package.json +61 -0
  5. package/src/cli.d.ts +25 -0
  6. package/src/cli.d.ts.map +1 -0
  7. package/src/cli.js +78 -0
  8. package/src/cli.js.map +1 -0
  9. package/src/commands/daemon.d.ts +11 -0
  10. package/src/commands/daemon.d.ts.map +1 -0
  11. package/src/commands/daemon.js +107 -0
  12. package/src/commands/daemon.js.map +1 -0
  13. package/src/commands/dev.d.ts +15 -0
  14. package/src/commands/dev.d.ts.map +1 -0
  15. package/src/commands/dev.js +387 -0
  16. package/src/commands/dev.js.map +1 -0
  17. package/src/commands/doctor.d.ts +11 -0
  18. package/src/commands/doctor.d.ts.map +1 -0
  19. package/src/commands/doctor.js +129 -0
  20. package/src/commands/doctor.js.map +1 -0
  21. package/src/commands/index.d.ts +12 -0
  22. package/src/commands/index.d.ts.map +1 -0
  23. package/src/commands/index.js +12 -0
  24. package/src/commands/index.js.map +1 -0
  25. package/src/commands/setup.d.ts +13 -0
  26. package/src/commands/setup.d.ts.map +1 -0
  27. package/src/commands/setup.js +203 -0
  28. package/src/commands/setup.js.map +1 -0
  29. package/src/commands/status.d.ts +11 -0
  30. package/src/commands/status.d.ts.map +1 -0
  31. package/src/commands/status.js +63 -0
  32. package/src/commands/status.js.map +1 -0
  33. package/src/commands/uninstall.d.ts +11 -0
  34. package/src/commands/uninstall.d.ts.map +1 -0
  35. package/src/commands/uninstall.js +164 -0
  36. package/src/commands/uninstall.js.map +1 -0
  37. package/src/detect/index.d.ts +9 -0
  38. package/src/detect/index.d.ts.map +1 -0
  39. package/src/detect/index.js +9 -0
  40. package/src/detect/index.js.map +1 -0
  41. package/src/dev-tui/DevApp.d.ts +13 -0
  42. package/src/dev-tui/DevApp.d.ts.map +1 -0
  43. package/src/dev-tui/DevApp.js +118 -0
  44. package/src/dev-tui/DevApp.js.map +1 -0
  45. package/src/dev-tui/DevSetupApp.d.ts +22 -0
  46. package/src/dev-tui/DevSetupApp.d.ts.map +1 -0
  47. package/src/dev-tui/DevSetupApp.js +407 -0
  48. package/src/dev-tui/DevSetupApp.js.map +1 -0
  49. package/src/dev-tui/components/ActionMenu.d.ts +11 -0
  50. package/src/dev-tui/components/ActionMenu.d.ts.map +1 -0
  51. package/src/dev-tui/components/ActionMenu.js +25 -0
  52. package/src/dev-tui/components/ActionMenu.js.map +1 -0
  53. package/src/dev-tui/components/ActionResult.d.ts +12 -0
  54. package/src/dev-tui/components/ActionResult.d.ts.map +1 -0
  55. package/src/dev-tui/components/ActionResult.js +27 -0
  56. package/src/dev-tui/components/ActionResult.js.map +1 -0
  57. package/src/dev-tui/components/DevConfirm.d.ts +22 -0
  58. package/src/dev-tui/components/DevConfirm.d.ts.map +1 -0
  59. package/src/dev-tui/components/DevConfirm.js +73 -0
  60. package/src/dev-tui/components/DevConfirm.js.map +1 -0
  61. package/src/dev-tui/components/DevModeSelect.d.ts +12 -0
  62. package/src/dev-tui/components/DevModeSelect.d.ts.map +1 -0
  63. package/src/dev-tui/components/DevModeSelect.js +69 -0
  64. package/src/dev-tui/components/DevModeSelect.js.map +1 -0
  65. package/src/dev-tui/components/LogViewer.d.ts +11 -0
  66. package/src/dev-tui/components/LogViewer.d.ts.map +1 -0
  67. package/src/dev-tui/components/LogViewer.js +18 -0
  68. package/src/dev-tui/components/LogViewer.js.map +1 -0
  69. package/src/dev-tui/components/PathPrompt.d.ts +13 -0
  70. package/src/dev-tui/components/PathPrompt.d.ts.map +1 -0
  71. package/src/dev-tui/components/PathPrompt.js +48 -0
  72. package/src/dev-tui/components/PathPrompt.js.map +1 -0
  73. package/src/dev-tui/components/StatusBar.d.ts +13 -0
  74. package/src/dev-tui/components/StatusBar.d.ts.map +1 -0
  75. package/src/dev-tui/components/StatusBar.js +36 -0
  76. package/src/dev-tui/components/StatusBar.js.map +1 -0
  77. package/src/dev-tui/index.d.ts +7 -0
  78. package/src/dev-tui/index.d.ts.map +1 -0
  79. package/src/dev-tui/index.js +5 -0
  80. package/src/dev-tui/index.js.map +1 -0
  81. package/src/dev-tui/runner.d.ts +17 -0
  82. package/src/dev-tui/runner.d.ts.map +1 -0
  83. package/src/dev-tui/runner.js +53 -0
  84. package/src/dev-tui/runner.js.map +1 -0
  85. package/src/dev-tui/state.d.ts +34 -0
  86. package/src/dev-tui/state.d.ts.map +1 -0
  87. package/src/dev-tui/state.js +77 -0
  88. package/src/dev-tui/state.js.map +1 -0
  89. package/src/index.d.ts +15 -0
  90. package/src/index.d.ts.map +1 -0
  91. package/src/index.js +19 -0
  92. package/src/index.js.map +1 -0
  93. package/src/setup-server/index.d.ts +8 -0
  94. package/src/setup-server/index.d.ts.map +1 -0
  95. package/src/setup-server/index.js +7 -0
  96. package/src/setup-server/index.js.map +1 -0
  97. package/src/setup-server/routes.d.ts +22 -0
  98. package/src/setup-server/routes.d.ts.map +1 -0
  99. package/src/setup-server/routes.js +279 -0
  100. package/src/setup-server/routes.js.map +1 -0
  101. package/src/setup-server/server.d.ts +16 -0
  102. package/src/setup-server/server.d.ts.map +1 -0
  103. package/src/setup-server/server.js +125 -0
  104. package/src/setup-server/server.js.map +1 -0
  105. package/src/setup-server/sse.d.ts +31 -0
  106. package/src/setup-server/sse.d.ts.map +1 -0
  107. package/src/setup-server/sse.js +76 -0
  108. package/src/setup-server/sse.js.map +1 -0
  109. package/src/setup-server/static.d.ts +11 -0
  110. package/src/setup-server/static.d.ts.map +1 -0
  111. package/src/setup-server/static.js +33 -0
  112. package/src/setup-server/static.js.map +1 -0
  113. package/src/utils/daemon.d.ts +63 -0
  114. package/src/utils/daemon.d.ts.map +1 -0
  115. package/src/utils/daemon.js +377 -0
  116. package/src/utils/daemon.js.map +1 -0
  117. package/src/utils/find-test-harness.d.ts +5 -0
  118. package/src/utils/find-test-harness.d.ts.map +1 -0
  119. package/src/utils/find-test-harness.js +23 -0
  120. package/src/utils/find-test-harness.js.map +1 -0
  121. package/src/utils/index.d.ts +8 -0
  122. package/src/utils/index.d.ts.map +1 -0
  123. package/src/utils/index.js +8 -0
  124. package/src/utils/index.js.map +1 -0
  125. package/src/utils/privileges.d.ts +51 -0
  126. package/src/utils/privileges.d.ts.map +1 -0
  127. package/src/utils/privileges.js +125 -0
  128. package/src/utils/privileges.js.map +1 -0
  129. package/src/utils/sudo-env.d.ts +27 -0
  130. package/src/utils/sudo-env.d.ts.map +1 -0
  131. package/src/utils/sudo-env.js +63 -0
  132. package/src/utils/sudo-env.js.map +1 -0
  133. package/src/wizard/App.d.ts +6 -0
  134. package/src/wizard/App.d.ts.map +1 -0
  135. package/src/wizard/App.js +215 -0
  136. package/src/wizard/App.js.map +1 -0
  137. package/src/wizard/Uninstall.d.ts +16 -0
  138. package/src/wizard/Uninstall.d.ts.map +1 -0
  139. package/src/wizard/Uninstall.js +163 -0
  140. package/src/wizard/Uninstall.js.map +1 -0
  141. package/src/wizard/components/AdvancedConfig.d.ts +42 -0
  142. package/src/wizard/components/AdvancedConfig.d.ts.map +1 -0
  143. package/src/wizard/components/AdvancedConfig.js +131 -0
  144. package/src/wizard/components/AdvancedConfig.js.map +1 -0
  145. package/src/wizard/components/Confirm.d.ts +31 -0
  146. package/src/wizard/components/Confirm.d.ts.map +1 -0
  147. package/src/wizard/components/Confirm.js +148 -0
  148. package/src/wizard/components/Confirm.js.map +1 -0
  149. package/src/wizard/components/Header.d.ts +6 -0
  150. package/src/wizard/components/Header.d.ts.map +1 -0
  151. package/src/wizard/components/Header.js +11 -0
  152. package/src/wizard/components/Header.js.map +1 -0
  153. package/src/wizard/components/ModeSelect.d.ts +12 -0
  154. package/src/wizard/components/ModeSelect.d.ts.map +1 -0
  155. package/src/wizard/components/ModeSelect.js +74 -0
  156. package/src/wizard/components/ModeSelect.js.map +1 -0
  157. package/src/wizard/components/PasscodeSetup.d.ts +16 -0
  158. package/src/wizard/components/PasscodeSetup.d.ts.map +1 -0
  159. package/src/wizard/components/PasscodeSetup.js +119 -0
  160. package/src/wizard/components/PasscodeSetup.js.map +1 -0
  161. package/src/wizard/components/ProgressBar.d.ts +12 -0
  162. package/src/wizard/components/ProgressBar.d.ts.map +1 -0
  163. package/src/wizard/components/ProgressBar.js +18 -0
  164. package/src/wizard/components/ProgressBar.js.map +1 -0
  165. package/src/wizard/components/StepList.d.ts +12 -0
  166. package/src/wizard/components/StepList.d.ts.map +1 -0
  167. package/src/wizard/components/StepList.js +37 -0
  168. package/src/wizard/components/StepList.js.map +1 -0
  169. package/src/wizard/components/Summary.d.ts +12 -0
  170. package/src/wizard/components/Summary.d.ts.map +1 -0
  171. package/src/wizard/components/Summary.js +44 -0
  172. package/src/wizard/components/Summary.js.map +1 -0
  173. package/src/wizard/engine.d.ts +29 -0
  174. package/src/wizard/engine.d.ts.map +1 -0
  175. package/src/wizard/engine.js +866 -0
  176. package/src/wizard/engine.js.map +1 -0
  177. package/src/wizard/index.d.ts +8 -0
  178. package/src/wizard/index.d.ts.map +1 -0
  179. package/src/wizard/index.js +8 -0
  180. package/src/wizard/index.js.map +1 -0
  181. package/src/wizard/types.d.ts +190 -0
  182. package/src/wizard/types.d.ts.map +1 -0
  183. package/src/wizard/types.js +193 -0
  184. package/src/wizard/types.js.map +1 -0
@@ -0,0 +1,377 @@
1
+ /**
2
+ * Daemon management utilities
3
+ *
4
+ * Provides functions for starting, stopping, and monitoring the AgenShield daemon.
5
+ */
6
+ import { spawn, execSync } from 'node:child_process';
7
+ import * as fs from 'node:fs';
8
+ import * as os from 'node:os';
9
+ import * as path from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import { isSecretEnvVar } from '@agenshield/sandbox';
12
+ import { captureCallingUserEnv } from './sudo-env.js';
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ /**
16
+ * Daemon configuration
17
+ */
18
+ export const DAEMON_CONFIG = {
19
+ PID_FILE: '/var/run/agenshield/agenshield.pid',
20
+ PORT: 5200,
21
+ HOST: '127.0.0.1', // Use IPv4 for actual connections (avoids IPv6 issues)
22
+ DISPLAY_HOST: 'localhost', // Use localhost for user-facing URLs
23
+ LOG_DIR: '/var/log/agenshield',
24
+ SOCKET_DIR: '/var/run/agenshield',
25
+ };
26
+ /**
27
+ * Get the current daemon status
28
+ */
29
+ export async function getDaemonStatus() {
30
+ // Check via HTTP health endpoint first
31
+ try {
32
+ const controller = new AbortController();
33
+ const timeout = setTimeout(() => controller.abort(), 2000);
34
+ const response = await fetch(`http://${DAEMON_CONFIG.HOST}:${DAEMON_CONFIG.PORT}/api/health`, {
35
+ signal: controller.signal,
36
+ });
37
+ clearTimeout(timeout);
38
+ if (response.ok) {
39
+ const data = (await response.json());
40
+ const pid = findDaemonPid() || findDaemonPidByPort(DAEMON_CONFIG.PORT);
41
+ return {
42
+ running: true,
43
+ pid: pid ?? undefined,
44
+ port: DAEMON_CONFIG.PORT,
45
+ uptime: data.uptime,
46
+ url: `http://${DAEMON_CONFIG.HOST}:${DAEMON_CONFIG.PORT}`,
47
+ };
48
+ }
49
+ }
50
+ catch {
51
+ // Daemon not responding via HTTP
52
+ }
53
+ // Check PID files (home dir + legacy location)
54
+ const pid = findDaemonPid();
55
+ if (pid) {
56
+ return { running: true, pid };
57
+ }
58
+ return { running: false };
59
+ }
60
+ /**
61
+ * Find the daemon executable path
62
+ */
63
+ export function findDaemonExecutable() {
64
+ const searchPaths = [
65
+ // Relative to CLI dist
66
+ path.join(__dirname, '../../../shield-daemon/dist/main.js'),
67
+ // Installed location
68
+ '/opt/agenshield/bin/agenshield-daemon',
69
+ // Development location from project root
70
+ path.join(process.cwd(), 'libs/shield-daemon/dist/main.js'),
71
+ ];
72
+ for (const p of searchPaths) {
73
+ if (fs.existsSync(p)) {
74
+ return p;
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+ /**
80
+ * Find the daemon TypeScript source (for tsx fallback in dev)
81
+ */
82
+ export function findDaemonSource() {
83
+ const searchPaths = [
84
+ path.join(__dirname, '../../../shield-daemon/src/main.ts'),
85
+ path.join(process.cwd(), 'libs/shield-daemon/src/main.ts'),
86
+ ];
87
+ return searchPaths.find(p => fs.existsSync(p)) || null;
88
+ }
89
+ /**
90
+ * Find tsx binary for running TypeScript directly
91
+ */
92
+ function findTsx() {
93
+ const searchPaths = [
94
+ path.join(process.cwd(), 'node_modules/.bin/tsx'),
95
+ ];
96
+ return searchPaths.find(p => fs.existsSync(p)) || null;
97
+ }
98
+ /**
99
+ * Find daemon PID from known PID file locations
100
+ */
101
+ function findDaemonPid() {
102
+ const homePidPath = path.join(os.homedir(), '.agenshield', 'daemon.pid');
103
+ const legacyPidPath = DAEMON_CONFIG.PID_FILE;
104
+ const pidPaths = [homePidPath, legacyPidPath];
105
+ // When running as root via sudo, the daemon runs as SUDO_USER and writes
106
+ // its PID to ~sudouser/.agenshield/daemon.pid (not /var/root/).
107
+ const sudoUser = process.env['SUDO_USER'];
108
+ if (sudoUser) {
109
+ try {
110
+ const userHome = execSync(`eval echo ~${sudoUser}`, {
111
+ encoding: 'utf-8',
112
+ stdio: ['pipe', 'pipe', 'pipe'],
113
+ timeout: 3000,
114
+ }).trim();
115
+ pidPaths.splice(1, 0, path.join(userHome, '.agenshield', 'daemon.pid'));
116
+ }
117
+ catch { /* ignore */ }
118
+ }
119
+ for (const pidPath of pidPaths) {
120
+ try {
121
+ if (fs.existsSync(pidPath)) {
122
+ const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);
123
+ if (!isNaN(pid)) {
124
+ process.kill(pid, 0); // throws if not running
125
+ return pid;
126
+ }
127
+ }
128
+ }
129
+ catch { /* stale or inaccessible */ }
130
+ }
131
+ return null;
132
+ }
133
+ /**
134
+ * Find daemon PID by checking which process is listening on the daemon port
135
+ */
136
+ function findDaemonPidByPort(port) {
137
+ try {
138
+ const output = execSync(`lsof -ti :${port}`, {
139
+ encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000,
140
+ }).trim();
141
+ if (output) {
142
+ const pid = parseInt(output.split('\n')[0], 10);
143
+ if (!isNaN(pid))
144
+ return pid;
145
+ }
146
+ }
147
+ catch { /* lsof failed */ }
148
+ return null;
149
+ }
150
+ /**
151
+ * Start the daemon
152
+ */
153
+ export async function startDaemon(options = {}) {
154
+ // Check if already running
155
+ const status = await getDaemonStatus();
156
+ if (status.running) {
157
+ return {
158
+ success: true,
159
+ message: 'Daemon is already running',
160
+ pid: status.pid,
161
+ };
162
+ }
163
+ // Find daemon executable
164
+ let daemonPath = findDaemonExecutable();
165
+ let runner = 'node';
166
+ if (!daemonPath) {
167
+ // Fallback: run from TypeScript source via tsx
168
+ const source = findDaemonSource();
169
+ const tsx = findTsx();
170
+ if (source && tsx) {
171
+ daemonPath = source;
172
+ runner = tsx;
173
+ }
174
+ else {
175
+ return {
176
+ success: false,
177
+ message: 'Daemon executable not found. Build first: npx nx build shield-daemon',
178
+ };
179
+ }
180
+ }
181
+ const env = {
182
+ ...process.env,
183
+ AGENSHIELD_PORT: String(DAEMON_CONFIG.PORT),
184
+ AGENSHIELD_HOST: DAEMON_CONFIG.HOST,
185
+ };
186
+ const isUnderSudo = !!process.env['SUDO_USER'];
187
+ if (isUnderSudo) {
188
+ // Legacy path: running via "sudo agenshield daemon start"
189
+ const userEnv = captureCallingUserEnv();
190
+ if (userEnv) {
191
+ const secretNames = Object.keys(userEnv).filter(k => userEnv[k] && isSecretEnvVar(k));
192
+ if (secretNames.length > 0) {
193
+ env['AGENSHIELD_USER_SECRETS'] = secretNames.join(',');
194
+ }
195
+ if (userEnv['PATH']) {
196
+ env['PATH'] = userEnv['PATH'];
197
+ }
198
+ }
199
+ }
200
+ else {
201
+ // User-mode: process.env already has correct PATH and secrets
202
+ const secretNames = Object.keys(process.env).filter(k => process.env[k] && isSecretEnvVar(k));
203
+ if (secretNames.length > 0) {
204
+ env['AGENSHIELD_USER_SECRETS'] = secretNames.join(',');
205
+ }
206
+ }
207
+ // Ensure system dirs exist and are writable (daemon runs as root)
208
+ for (const dir of [DAEMON_CONFIG.LOG_DIR, DAEMON_CONFIG.SOCKET_DIR]) {
209
+ try {
210
+ fs.mkdirSync(dir, { recursive: true });
211
+ }
212
+ catch {
213
+ // May already exist
214
+ }
215
+ }
216
+ // Ensure user config dir
217
+ const configDir = path.join(os.homedir(), '.agenshield');
218
+ fs.mkdirSync(configDir, { recursive: true });
219
+ if (options.foreground) {
220
+ // Run in foreground (blocking)
221
+ const spawnOpts = {
222
+ stdio: 'inherit',
223
+ env,
224
+ };
225
+ const child = spawn(runner, [daemonPath], spawnOpts);
226
+ return new Promise((resolve) => {
227
+ child.on('exit', (code) => {
228
+ resolve({
229
+ success: code === 0,
230
+ message: code === 0 ? 'Daemon exited' : `Daemon exited with code ${code}`,
231
+ });
232
+ });
233
+ });
234
+ }
235
+ // Run in background
236
+ try {
237
+ // Try launchctl first (macOS preferred)
238
+ try {
239
+ execSync('launchctl list com.agenshield.daemon 2>/dev/null', { stdio: 'pipe' });
240
+ execSync('launchctl start com.agenshield.daemon');
241
+ return {
242
+ success: true,
243
+ message: 'Daemon started via launchd',
244
+ };
245
+ }
246
+ catch {
247
+ // Not using launchd, fall back to nohup
248
+ }
249
+ let logDir = env['AGENSHIELD_LOG_DIR'] || DAEMON_CONFIG.LOG_DIR;
250
+ let logFile = path.join(logDir, 'daemon.log');
251
+ let logFd;
252
+ try {
253
+ logFd = fs.openSync(logFile, 'a');
254
+ }
255
+ catch {
256
+ // File open failed — fall back to user-local log
257
+ logDir = path.join(os.homedir(), '.agenshield', 'logs');
258
+ fs.mkdirSync(logDir, { recursive: true });
259
+ logFile = path.join(logDir, 'daemon.log');
260
+ logFd = fs.openSync(logFile, 'a');
261
+ }
262
+ const bgSpawnOpts = {
263
+ detached: true,
264
+ stdio: ['ignore', logFd, logFd],
265
+ env,
266
+ };
267
+ const child = spawn(runner, [daemonPath], bgSpawnOpts);
268
+ child.unref();
269
+ // Write PID file
270
+ try {
271
+ fs.writeFileSync(DAEMON_CONFIG.PID_FILE, String(child.pid));
272
+ }
273
+ catch {
274
+ // May require sudo
275
+ }
276
+ // Wait a moment and verify
277
+ await new Promise((resolve) => setTimeout(resolve, 1000));
278
+ const newStatus = await getDaemonStatus();
279
+ if (newStatus.running) {
280
+ return {
281
+ success: true,
282
+ message: 'Daemon started',
283
+ pid: child.pid,
284
+ };
285
+ }
286
+ else {
287
+ return {
288
+ success: false,
289
+ message: `Daemon failed to start. Check logs at ${logFile}`,
290
+ };
291
+ }
292
+ }
293
+ catch (err) {
294
+ return {
295
+ success: false,
296
+ message: `Failed to start daemon: ${err.message}`,
297
+ };
298
+ }
299
+ }
300
+ /**
301
+ * Stop the daemon
302
+ */
303
+ export async function stopDaemon() {
304
+ const status = await getDaemonStatus();
305
+ if (!status.running) {
306
+ return {
307
+ success: true,
308
+ message: 'Daemon is not running',
309
+ };
310
+ }
311
+ // Try launchctl first
312
+ try {
313
+ execSync('launchctl list com.agenshield.daemon 2>/dev/null', { stdio: 'pipe' });
314
+ execSync('launchctl stop com.agenshield.daemon');
315
+ return {
316
+ success: true,
317
+ message: 'Daemon stopped via launchd',
318
+ };
319
+ }
320
+ catch {
321
+ // Not using launchd
322
+ }
323
+ // Try killing by PID
324
+ if (status.pid) {
325
+ try {
326
+ process.kill(status.pid, 'SIGTERM');
327
+ // Wait for process to exit
328
+ await new Promise((resolve) => setTimeout(resolve, 1000));
329
+ // Clean up PID file
330
+ try {
331
+ fs.unlinkSync(DAEMON_CONFIG.PID_FILE);
332
+ }
333
+ catch {
334
+ // Ignore
335
+ }
336
+ return {
337
+ success: true,
338
+ message: `Daemon stopped (PID ${status.pid})`,
339
+ };
340
+ }
341
+ catch (err) {
342
+ return {
343
+ success: false,
344
+ message: `Failed to stop daemon: ${err.message}`,
345
+ };
346
+ }
347
+ }
348
+ // Fallback: find PID by port
349
+ const portPid = findDaemonPidByPort(DAEMON_CONFIG.PORT);
350
+ if (portPid) {
351
+ try {
352
+ process.kill(portPid, 'SIGTERM');
353
+ await new Promise(r => setTimeout(r, 1000));
354
+ return { success: true, message: `Daemon stopped (PID ${portPid}, via port lookup)` };
355
+ }
356
+ catch (err) {
357
+ return { success: false, message: `Failed to stop daemon: ${err.message}` };
358
+ }
359
+ }
360
+ return {
361
+ success: false,
362
+ message: 'Could not determine daemon PID. Try: pkill -f agenshield-daemon',
363
+ };
364
+ }
365
+ /**
366
+ * Restart the daemon
367
+ */
368
+ export async function restartDaemon() {
369
+ const stopResult = await stopDaemon();
370
+ if (!stopResult.success && stopResult.message !== 'Daemon is not running') {
371
+ return stopResult;
372
+ }
373
+ await new Promise((resolve) => setTimeout(resolve, 500));
374
+ const startResult = await startDaemon({ foreground: false });
375
+ return startResult;
376
+ }
377
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../../../src/utils/daemon.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Daemon management utilities\n *\n * Provides functions for starting, stopping, and monitoring the AgenShield daemon.\n */\n\nimport { spawn, execSync } from 'node:child_process';\nimport type { SpawnOptions } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { isSecretEnvVar } from '@agenshield/sandbox';\nimport { captureCallingUserEnv } from './sudo-env.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/**\n * Daemon configuration\n */\nexport const DAEMON_CONFIG = {\n PID_FILE: '/var/run/agenshield/agenshield.pid',\n PORT: 5200,\n HOST: '127.0.0.1', // Use IPv4 for actual connections (avoids IPv6 issues)\n DISPLAY_HOST: 'localhost', // Use localhost for user-facing URLs\n LOG_DIR: '/var/log/agenshield',\n SOCKET_DIR: '/var/run/agenshield',\n};\n\n/**\n * Status of the daemon\n */\nexport interface DaemonStatus {\n running: boolean;\n pid?: number;\n port?: number;\n uptime?: string;\n url?: string;\n}\n\n/**\n * Get the current daemon status\n */\nexport async function getDaemonStatus(): Promise<DaemonStatus> {\n // Check via HTTP health endpoint first\n try {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 2000);\n\n const response = await fetch(`http://${DAEMON_CONFIG.HOST}:${DAEMON_CONFIG.PORT}/api/health`, {\n signal: controller.signal,\n });\n clearTimeout(timeout);\n\n if (response.ok) {\n const data = (await response.json()) as { uptime?: string };\n const pid = findDaemonPid() || findDaemonPidByPort(DAEMON_CONFIG.PORT);\n return {\n running: true,\n pid: pid ?? undefined,\n port: DAEMON_CONFIG.PORT,\n uptime: data.uptime,\n url: `http://${DAEMON_CONFIG.HOST}:${DAEMON_CONFIG.PORT}`,\n };\n }\n } catch {\n // Daemon not responding via HTTP\n }\n\n // Check PID files (home dir + legacy location)\n const pid = findDaemonPid();\n if (pid) {\n return { running: true, pid };\n }\n\n return { running: false };\n}\n\n/**\n * Find the daemon executable path\n */\nexport function findDaemonExecutable(): string | null {\n const searchPaths = [\n // Relative to CLI dist\n path.join(__dirname, '../../../shield-daemon/dist/main.js'),\n // Installed location\n '/opt/agenshield/bin/agenshield-daemon',\n // Development location from project root\n path.join(process.cwd(), 'libs/shield-daemon/dist/main.js'),\n ];\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) {\n return p;\n }\n }\n\n return null;\n}\n\n/**\n * Find the daemon TypeScript source (for tsx fallback in dev)\n */\nexport function findDaemonSource(): string | null {\n const searchPaths = [\n path.join(__dirname, '../../../shield-daemon/src/main.ts'),\n path.join(process.cwd(), 'libs/shield-daemon/src/main.ts'),\n ];\n return searchPaths.find(p => fs.existsSync(p)) || null;\n}\n\n/**\n * Find tsx binary for running TypeScript directly\n */\nfunction findTsx(): string | null {\n const searchPaths = [\n path.join(process.cwd(), 'node_modules/.bin/tsx'),\n ];\n return searchPaths.find(p => fs.existsSync(p)) || null;\n}\n\n/**\n * Find daemon PID from known PID file locations\n */\nfunction findDaemonPid(): number | null {\n const homePidPath = path.join(os.homedir(), '.agenshield', 'daemon.pid');\n const legacyPidPath = DAEMON_CONFIG.PID_FILE;\n const pidPaths = [homePidPath, legacyPidPath];\n\n // When running as root via sudo, the daemon runs as SUDO_USER and writes\n // its PID to ~sudouser/.agenshield/daemon.pid (not /var/root/).\n const sudoUser = process.env['SUDO_USER'];\n if (sudoUser) {\n try {\n const userHome = execSync(`eval echo ~${sudoUser}`, {\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n timeout: 3000,\n }).trim();\n pidPaths.splice(1, 0, path.join(userHome, '.agenshield', 'daemon.pid'));\n } catch { /* ignore */ }\n }\n\n for (const pidPath of pidPaths) {\n try {\n if (fs.existsSync(pidPath)) {\n const pid = parseInt(fs.readFileSync(pidPath, 'utf8').trim(), 10);\n if (!isNaN(pid)) {\n process.kill(pid, 0); // throws if not running\n return pid;\n }\n }\n } catch { /* stale or inaccessible */ }\n }\n return null;\n}\n\n/**\n * Find daemon PID by checking which process is listening on the daemon port\n */\nfunction findDaemonPidByPort(port: number): number | null {\n try {\n const output = execSync(`lsof -ti :${port}`, {\n encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 3000,\n }).trim();\n if (output) {\n const pid = parseInt(output.split('\\n')[0], 10);\n if (!isNaN(pid)) return pid;\n }\n } catch { /* lsof failed */ }\n return null;\n}\n\n/**\n * Start the daemon\n */\nexport async function startDaemon(options: { foreground?: boolean } = {}): Promise<{\n success: boolean;\n message: string;\n pid?: number;\n}> {\n // Check if already running\n const status = await getDaemonStatus();\n if (status.running) {\n return {\n success: true,\n message: 'Daemon is already running',\n pid: status.pid,\n };\n }\n\n // Find daemon executable\n let daemonPath = findDaemonExecutable();\n let runner = 'node';\n\n if (!daemonPath) {\n // Fallback: run from TypeScript source via tsx\n const source = findDaemonSource();\n const tsx = findTsx();\n if (source && tsx) {\n daemonPath = source;\n runner = tsx;\n } else {\n return {\n success: false,\n message: 'Daemon executable not found. Build first: npx nx build shield-daemon',\n };\n }\n }\n\n const env: Record<string, string | undefined> = {\n ...process.env,\n AGENSHIELD_PORT: String(DAEMON_CONFIG.PORT),\n AGENSHIELD_HOST: DAEMON_CONFIG.HOST,\n };\n\n const isUnderSudo = !!process.env['SUDO_USER'];\n\n if (isUnderSudo) {\n // Legacy path: running via \"sudo agenshield daemon start\"\n const userEnv = captureCallingUserEnv();\n if (userEnv) {\n const secretNames = Object.keys(userEnv).filter(k => userEnv[k] && isSecretEnvVar(k));\n if (secretNames.length > 0) {\n env['AGENSHIELD_USER_SECRETS'] = secretNames.join(',');\n }\n if (userEnv['PATH']) {\n env['PATH'] = userEnv['PATH'];\n }\n }\n } else {\n // User-mode: process.env already has correct PATH and secrets\n const secretNames = Object.keys(process.env).filter(\n k => process.env[k] && isSecretEnvVar(k)\n );\n if (secretNames.length > 0) {\n env['AGENSHIELD_USER_SECRETS'] = secretNames.join(',');\n }\n }\n\n // Ensure system dirs exist and are writable (daemon runs as root)\n for (const dir of [DAEMON_CONFIG.LOG_DIR, DAEMON_CONFIG.SOCKET_DIR]) {\n try {\n fs.mkdirSync(dir, { recursive: true });\n } catch {\n // May already exist\n }\n }\n\n // Ensure user config dir\n const configDir = path.join(os.homedir(), '.agenshield');\n fs.mkdirSync(configDir, { recursive: true });\n\n if (options.foreground) {\n // Run in foreground (blocking)\n const spawnOpts: SpawnOptions = {\n stdio: 'inherit',\n env,\n };\n const child = spawn(runner, [daemonPath], spawnOpts);\n\n return new Promise((resolve) => {\n child.on('exit', (code) => {\n resolve({\n success: code === 0,\n message: code === 0 ? 'Daemon exited' : `Daemon exited with code ${code}`,\n });\n });\n });\n }\n\n // Run in background\n try {\n // Try launchctl first (macOS preferred)\n try {\n execSync('launchctl list com.agenshield.daemon 2>/dev/null', { stdio: 'pipe' });\n execSync('launchctl start com.agenshield.daemon');\n return {\n success: true,\n message: 'Daemon started via launchd',\n };\n } catch {\n // Not using launchd, fall back to nohup\n }\n\n let logDir = env['AGENSHIELD_LOG_DIR'] || DAEMON_CONFIG.LOG_DIR;\n let logFile = path.join(logDir, 'daemon.log');\n let logFd: number;\n try {\n logFd = fs.openSync(logFile, 'a');\n } catch {\n // File open failed — fall back to user-local log\n logDir = path.join(os.homedir(), '.agenshield', 'logs');\n fs.mkdirSync(logDir, { recursive: true });\n logFile = path.join(logDir, 'daemon.log');\n logFd = fs.openSync(logFile, 'a');\n }\n\n const bgSpawnOpts: SpawnOptions = {\n detached: true,\n stdio: ['ignore', logFd, logFd],\n env,\n };\n const child = spawn(runner, [daemonPath], bgSpawnOpts);\n\n child.unref();\n\n // Write PID file\n try {\n fs.writeFileSync(DAEMON_CONFIG.PID_FILE, String(child.pid));\n } catch {\n // May require sudo\n }\n\n // Wait a moment and verify\n await new Promise((resolve) => setTimeout(resolve, 1000));\n const newStatus = await getDaemonStatus();\n\n if (newStatus.running) {\n return {\n success: true,\n message: 'Daemon started',\n pid: child.pid,\n };\n } else {\n return {\n success: false,\n message: `Daemon failed to start. Check logs at ${logFile}`,\n };\n }\n } catch (err) {\n return {\n success: false,\n message: `Failed to start daemon: ${(err as Error).message}`,\n };\n }\n}\n\n/**\n * Stop the daemon\n */\nexport async function stopDaemon(): Promise<{\n success: boolean;\n message: string;\n}> {\n const status = await getDaemonStatus();\n\n if (!status.running) {\n return {\n success: true,\n message: 'Daemon is not running',\n };\n }\n\n // Try launchctl first\n try {\n execSync('launchctl list com.agenshield.daemon 2>/dev/null', { stdio: 'pipe' });\n execSync('launchctl stop com.agenshield.daemon');\n return {\n success: true,\n message: 'Daemon stopped via launchd',\n };\n } catch {\n // Not using launchd\n }\n\n // Try killing by PID\n if (status.pid) {\n try {\n process.kill(status.pid, 'SIGTERM');\n\n // Wait for process to exit\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n // Clean up PID file\n try {\n fs.unlinkSync(DAEMON_CONFIG.PID_FILE);\n } catch {\n // Ignore\n }\n\n return {\n success: true,\n message: `Daemon stopped (PID ${status.pid})`,\n };\n } catch (err) {\n return {\n success: false,\n message: `Failed to stop daemon: ${(err as Error).message}`,\n };\n }\n }\n\n // Fallback: find PID by port\n const portPid = findDaemonPidByPort(DAEMON_CONFIG.PORT);\n if (portPid) {\n try {\n process.kill(portPid, 'SIGTERM');\n await new Promise(r => setTimeout(r, 1000));\n return { success: true, message: `Daemon stopped (PID ${portPid}, via port lookup)` };\n } catch (err) {\n return { success: false, message: `Failed to stop daemon: ${(err as Error).message}` };\n }\n }\n\n return {\n success: false,\n message: 'Could not determine daemon PID. Try: pkill -f agenshield-daemon',\n };\n}\n\n/**\n * Restart the daemon\n */\nexport async function restartDaemon(): Promise<{\n success: boolean;\n message: string;\n}> {\n const stopResult = await stopDaemon();\n if (!stopResult.success && stopResult.message !== 'Daemon is not running') {\n return stopResult;\n }\n\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n const startResult = await startDaemon({ foreground: false });\n return startResult;\n}\n"]}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Locate the test harness binary path
3
+ */
4
+ export declare function findTestHarness(): string | null;
5
+ //# sourceMappingURL=find-test-harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-test-harness.d.ts","sourceRoot":"","sources":["../../../src/utils/find-test-harness.ts"],"names":[],"mappings":"AAAA;;GAEG;AASH,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAe/C"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Locate the test harness binary path
3
+ */
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ export function findTestHarness() {
10
+ const searchPaths = [
11
+ // Development location from project root
12
+ path.join(process.cwd(), 'tools/test-harness/bin/dummy-openclaw.js'),
13
+ // Relative to CLI dist
14
+ path.join(__dirname, '../../../../tools/test-harness/bin/dummy-openclaw.js'),
15
+ ];
16
+ for (const p of searchPaths) {
17
+ if (fs.existsSync(p)) {
18
+ return path.resolve(p);
19
+ }
20
+ }
21
+ return null;
22
+ }
23
+ //# sourceMappingURL=find-test-harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-test-harness.js","sourceRoot":"","sources":["../../../src/utils/find-test-harness.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,MAAM,UAAU,eAAe;IAC7B,MAAM,WAAW,GAAG;QAClB,yCAAyC;QACzC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,0CAA0C,CAAC;QACpE,uBAAuB;QACvB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,sDAAsD,CAAC;KAC7E,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["/**\n * Locate the test harness binary path\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport function findTestHarness(): string | null {\n const searchPaths = [\n // Development location from project root\n path.join(process.cwd(), 'tools/test-harness/bin/dummy-openclaw.js'),\n // Relative to CLI dist\n path.join(__dirname, '../../../../tools/test-harness/bin/dummy-openclaw.js'),\n ];\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) {\n return path.resolve(p);\n }\n }\n\n return null;\n}\n"]}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CLI Utilities
3
+ */
4
+ export * from './privileges.js';
5
+ export * from './daemon.js';
6
+ export * from './find-test-harness.js';
7
+ export * from './sudo-env.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * CLI Utilities
3
+ */
4
+ export * from './privileges.js';
5
+ export * from './daemon.js';
6
+ export * from './find-test-harness.js';
7
+ export * from './sudo-env.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC;AACvC,cAAc,eAAe,CAAC","sourcesContent":["/**\n * CLI Utilities\n */\n\nexport * from './privileges.js';\nexport * from './daemon.js';\nexport * from './find-test-harness.js';\nexport * from './sudo-env.js';\n"]}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Privilege detection utilities
3
+ *
4
+ * Detects user privileges and provides utilities for privilege-related operations.
5
+ */
6
+ /**
7
+ * Information about the current user's privileges
8
+ */
9
+ export interface PrivilegeInfo {
10
+ /** Whether running as root (UID 0) */
11
+ isRoot: boolean;
12
+ /** Current user ID */
13
+ uid: number;
14
+ /** Current group ID */
15
+ gid: number;
16
+ /** Current username */
17
+ username: string;
18
+ /** Whether the user can use sudo */
19
+ canSudo: boolean;
20
+ /** Whether sudo can be used without a password */
21
+ sudoNoPassword: boolean;
22
+ }
23
+ /**
24
+ * Detect the current user's privileges
25
+ */
26
+ export declare function detectPrivileges(): PrivilegeInfo;
27
+ /**
28
+ * Ensure sudo credentials are cached so subsequent `sudo` child-process
29
+ * calls succeed without a TTY prompt.
30
+ * Call this once before a batch of privileged operations.
31
+ */
32
+ export declare function ensureSudoAccess(): void;
33
+ /**
34
+ * Start a keepalive interval that refreshes sudo credentials every 2 minutes
35
+ * so the 5-minute sudo cache doesn't expire during long-running setup phases.
36
+ */
37
+ export declare function startSudoKeepalive(): NodeJS.Timeout;
38
+ /**
39
+ * Check if a command requires root privileges
40
+ */
41
+ export declare function requiresRoot(command: string): boolean;
42
+ /**
43
+ * Print a warning about missing privileges
44
+ */
45
+ export declare function printPrivilegeWarning(command: string, priv?: PrivilegeInfo): void;
46
+ /**
47
+ * Ensure the command is running with root privileges
48
+ * Exits the process if not running as root
49
+ */
50
+ export declare function ensureRoot(command: string): void;
51
+ //# sourceMappingURL=privileges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privileges.d.ts","sourceRoot":"","sources":["../../../src/utils/privileges.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,MAAM,EAAE,OAAO,CAAC;IAChB,sBAAsB;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,uBAAuB;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAiChD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAmBvC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,OAAO,CAInD;AAQD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAoBjF;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMhD"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Privilege detection utilities
3
+ *
4
+ * Detects user privileges and provides utilities for privilege-related operations.
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import * as os from 'node:os';
8
+ /**
9
+ * Detect the current user's privileges
10
+ */
11
+ export function detectPrivileges() {
12
+ const uid = process.getuid?.() ?? -1;
13
+ const gid = process.getgid?.() ?? -1;
14
+ const username = os.userInfo().username;
15
+ const isRoot = uid === 0;
16
+ // Check if user can use sudo
17
+ let canSudo = false;
18
+ let sudoNoPassword = false;
19
+ if (!isRoot) {
20
+ try {
21
+ // Check if user is in admin/sudo group
22
+ const groups = execSync('groups', { encoding: 'utf8' });
23
+ canSudo = groups.includes('admin') || groups.includes('wheel') || groups.includes('sudo');
24
+ // Check if SUDO_ASKPASS is set
25
+ if (process.env['SUDO_ASKPASS']) {
26
+ sudoNoPassword = true;
27
+ }
28
+ }
29
+ catch {
30
+ // Ignore errors
31
+ }
32
+ }
33
+ return {
34
+ isRoot,
35
+ uid,
36
+ gid,
37
+ username,
38
+ canSudo,
39
+ sudoNoPassword,
40
+ };
41
+ }
42
+ /**
43
+ * Ensure sudo credentials are cached so subsequent `sudo` child-process
44
+ * calls succeed without a TTY prompt.
45
+ * Call this once before a batch of privileged operations.
46
+ */
47
+ export function ensureSudoAccess() {
48
+ const priv = detectPrivileges();
49
+ if (priv.isRoot)
50
+ return; // Already root, no need
51
+ // Check if sudo credentials are already cached (non-interactive check)
52
+ try {
53
+ execSync('sudo -n true', { stdio: 'pipe', timeout: 5_000 });
54
+ return; // Credentials already cached, no prompt needed
55
+ }
56
+ catch {
57
+ // Credentials not cached — need to prompt
58
+ }
59
+ console.log('\nThis operation requires administrator privileges.\n');
60
+ try {
61
+ execSync('sudo -v', { stdio: 'inherit', timeout: 60_000 });
62
+ }
63
+ catch {
64
+ console.error('Failed to obtain sudo access.');
65
+ process.exit(1);
66
+ }
67
+ }
68
+ /**
69
+ * Start a keepalive interval that refreshes sudo credentials every 2 minutes
70
+ * so the 5-minute sudo cache doesn't expire during long-running setup phases.
71
+ */
72
+ export function startSudoKeepalive() {
73
+ return setInterval(() => {
74
+ try {
75
+ execSync('sudo -v', { stdio: 'pipe', timeout: 2000 });
76
+ }
77
+ catch { /* ignore */ }
78
+ }, 120_000);
79
+ }
80
+ /**
81
+ * Commands that require root privileges
82
+ * (Empty — no command requires root upfront anymore; sudo is requested on demand)
83
+ */
84
+ const ROOT_COMMANDS = [];
85
+ /**
86
+ * Check if a command requires root privileges
87
+ */
88
+ export function requiresRoot(command) {
89
+ return ROOT_COMMANDS.some((c) => command.startsWith(c));
90
+ }
91
+ /**
92
+ * Print a warning about missing privileges
93
+ */
94
+ export function printPrivilegeWarning(command, priv) {
95
+ const p = priv ?? detectPrivileges();
96
+ console.log('\x1b[33m⚠ Warning: This command requires elevated privileges.\x1b[0m');
97
+ console.log('');
98
+ console.log('You are currently running as:');
99
+ console.log(` User: ${p.username} (UID: ${p.uid})`);
100
+ console.log('');
101
+ if (p.canSudo) {
102
+ console.log('Run with sudo:');
103
+ console.log(` \x1b[36msudo agenshield ${command}\x1b[0m`);
104
+ }
105
+ else {
106
+ console.log('You need administrator privileges. Either:');
107
+ console.log(' 1. Run as root user');
108
+ console.log(' 2. Add your user to the admin group');
109
+ console.log('');
110
+ console.log(` \x1b[36msudo agenshield ${command}\x1b[0m`);
111
+ }
112
+ console.log('');
113
+ }
114
+ /**
115
+ * Ensure the command is running with root privileges
116
+ * Exits the process if not running as root
117
+ */
118
+ export function ensureRoot(command) {
119
+ const priv = detectPrivileges();
120
+ if (!priv.isRoot) {
121
+ printPrivilegeWarning(command, priv);
122
+ process.exit(1);
123
+ }
124
+ }
125
+ //# sourceMappingURL=privileges.js.map