@edgible-team/cli 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +136 -0
  2. package/README.md +450 -0
  3. package/dist/client/api-client.js +1057 -0
  4. package/dist/client/index.js +21 -0
  5. package/dist/commands/agent.js +1280 -0
  6. package/dist/commands/ai.js +608 -0
  7. package/dist/commands/application.js +885 -0
  8. package/dist/commands/auth.js +570 -0
  9. package/dist/commands/base/BaseCommand.js +93 -0
  10. package/dist/commands/base/CommandHandler.js +7 -0
  11. package/dist/commands/base/command-wrapper.js +58 -0
  12. package/dist/commands/base/middleware.js +77 -0
  13. package/dist/commands/config.js +116 -0
  14. package/dist/commands/connectivity.js +59 -0
  15. package/dist/commands/debug.js +98 -0
  16. package/dist/commands/discover.js +144 -0
  17. package/dist/commands/examples/migrated-command-example.js +180 -0
  18. package/dist/commands/gateway.js +494 -0
  19. package/dist/commands/managedGateway.js +787 -0
  20. package/dist/commands/utils/config-validator.js +76 -0
  21. package/dist/commands/utils/gateway-prompt.js +79 -0
  22. package/dist/commands/utils/input-parser.js +120 -0
  23. package/dist/commands/utils/output-formatter.js +109 -0
  24. package/dist/config/app-config.js +99 -0
  25. package/dist/detection/SystemCapabilityDetector.js +1244 -0
  26. package/dist/detection/ToolDetector.js +305 -0
  27. package/dist/detection/WorkloadDetector.js +314 -0
  28. package/dist/di/bindings.js +99 -0
  29. package/dist/di/container.js +88 -0
  30. package/dist/di/types.js +32 -0
  31. package/dist/index.js +52 -0
  32. package/dist/interfaces/IDaemonManager.js +3 -0
  33. package/dist/repositories/config-repository.js +62 -0
  34. package/dist/repositories/gateway-repository.js +35 -0
  35. package/dist/scripts/postinstall.js +101 -0
  36. package/dist/services/AgentStatusManager.js +299 -0
  37. package/dist/services/ConnectivityTester.js +271 -0
  38. package/dist/services/DependencyInstaller.js +475 -0
  39. package/dist/services/LocalAgentManager.js +2216 -0
  40. package/dist/services/application/ApplicationService.js +299 -0
  41. package/dist/services/auth/AuthService.js +214 -0
  42. package/dist/services/aws.js +644 -0
  43. package/dist/services/daemon/DaemonManagerFactory.js +65 -0
  44. package/dist/services/daemon/DockerDaemonManager.js +395 -0
  45. package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
  46. package/dist/services/daemon/PodmanDaemonManager.js +369 -0
  47. package/dist/services/daemon/SystemdDaemonManager.js +221 -0
  48. package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
  49. package/dist/services/daemon/index.js +16 -0
  50. package/dist/services/edgible.js +3060 -0
  51. package/dist/services/gateway/GatewayService.js +334 -0
  52. package/dist/state/config.js +146 -0
  53. package/dist/types/AgentConfig.js +5 -0
  54. package/dist/types/AgentStatus.js +5 -0
  55. package/dist/types/ApiClient.js +5 -0
  56. package/dist/types/ApiRequests.js +5 -0
  57. package/dist/types/ApiResponses.js +5 -0
  58. package/dist/types/Application.js +5 -0
  59. package/dist/types/CaddyJson.js +5 -0
  60. package/dist/types/UnifiedAgentStatus.js +56 -0
  61. package/dist/types/WireGuard.js +5 -0
  62. package/dist/types/Workload.js +5 -0
  63. package/dist/types/agent.js +5 -0
  64. package/dist/types/command-options.js +5 -0
  65. package/dist/types/connectivity.js +5 -0
  66. package/dist/types/errors.js +250 -0
  67. package/dist/types/gateway-types.js +5 -0
  68. package/dist/types/index.js +48 -0
  69. package/dist/types/models/ApplicationData.js +5 -0
  70. package/dist/types/models/CertificateData.js +5 -0
  71. package/dist/types/models/DeviceData.js +5 -0
  72. package/dist/types/models/DevicePoolData.js +5 -0
  73. package/dist/types/models/OrganizationData.js +5 -0
  74. package/dist/types/models/OrganizationInviteData.js +5 -0
  75. package/dist/types/models/ProviderConfiguration.js +5 -0
  76. package/dist/types/models/ResourceData.js +5 -0
  77. package/dist/types/models/ServiceResourceData.js +5 -0
  78. package/dist/types/models/UserData.js +5 -0
  79. package/dist/types/route.js +5 -0
  80. package/dist/types/validation/schemas.js +218 -0
  81. package/dist/types/validation.js +5 -0
  82. package/dist/utils/FileIntegrityManager.js +256 -0
  83. package/dist/utils/PathMigration.js +219 -0
  84. package/dist/utils/PathResolver.js +235 -0
  85. package/dist/utils/PlatformDetector.js +277 -0
  86. package/dist/utils/console-logger.js +130 -0
  87. package/dist/utils/docker-compose-parser.js +179 -0
  88. package/dist/utils/errors.js +130 -0
  89. package/dist/utils/health-checker.js +155 -0
  90. package/dist/utils/json-logger.js +72 -0
  91. package/dist/utils/log-formatter.js +293 -0
  92. package/dist/utils/logger.js +59 -0
  93. package/dist/utils/network-utils.js +217 -0
  94. package/dist/utils/output.js +182 -0
  95. package/dist/utils/passwordValidation.js +91 -0
  96. package/dist/utils/progress.js +167 -0
  97. package/dist/utils/sudo-checker.js +22 -0
  98. package/dist/utils/urls.js +32 -0
  99. package/dist/utils/validation.js +31 -0
  100. package/dist/validation/schemas.js +175 -0
  101. package/dist/validation/validator.js +67 -0
  102. package/package.json +83 -0
@@ -0,0 +1,257 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LaunchdDaemonManager = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const util_1 = require("util");
39
+ const fs = __importStar(require("fs/promises"));
40
+ const path = __importStar(require("path"));
41
+ const PlatformDetector_1 = require("../../utils/PlatformDetector");
42
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
43
+ class LaunchdDaemonManager {
44
+ constructor() {
45
+ this.serviceName = 'com.edgible.agent';
46
+ if (PlatformDetector_1.PlatformDetector.getPlatform() !== 'darwin') {
47
+ throw new Error('LaunchdDaemonManager is only supported on macOS');
48
+ }
49
+ // Use LaunchDaemons for system-wide (root) installation
50
+ this.plistPath = `/Library/LaunchDaemons/${this.serviceName}.plist`;
51
+ this.logPath = '/var/log/edgible-agent';
52
+ }
53
+ async install(config) {
54
+ // Create log directory
55
+ try {
56
+ await execAsync(`mkdir -p ${this.logPath}`);
57
+ }
58
+ catch (error) {
59
+ throw new Error(`Failed to create log directory: ${error instanceof Error ? error.message : String(error)}`);
60
+ }
61
+ // Generate plist file
62
+ const plistContent = this.generatePlistFile(config);
63
+ // Write plist file (requires root)
64
+ try {
65
+ await fs.writeFile(this.plistPath, plistContent, 'utf8');
66
+ await execAsync(`chmod 644 ${this.plistPath}`);
67
+ await execAsync(`chown root:wheel ${this.plistPath}`);
68
+ }
69
+ catch (error) {
70
+ throw new Error(`Failed to write launchd plist file: ${error instanceof Error ? error.message : String(error)}`);
71
+ }
72
+ }
73
+ async start() {
74
+ try {
75
+ await execAsync(`launchctl load ${this.plistPath}`);
76
+ await execAsync(`launchctl start ${this.serviceName}`);
77
+ }
78
+ catch (error) {
79
+ throw new Error(`Failed to start service: ${error instanceof Error ? error.message : String(error)}`);
80
+ }
81
+ }
82
+ async stop() {
83
+ try {
84
+ await execAsync(`launchctl stop ${this.serviceName}`);
85
+ await execAsync(`launchctl unload ${this.plistPath}`);
86
+ }
87
+ catch (error) {
88
+ throw new Error(`Failed to stop service: ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ }
91
+ async restart() {
92
+ await this.stop();
93
+ // Wait a moment before restarting
94
+ await new Promise(resolve => setTimeout(resolve, 1000));
95
+ await this.start();
96
+ }
97
+ async status() {
98
+ try {
99
+ const { stdout } = await execAsync(`launchctl list | grep ${this.serviceName}`);
100
+ const parts = stdout.trim().split(/\s+/);
101
+ if (parts.length >= 3) {
102
+ const pidStr = parts[0];
103
+ const running = pidStr !== '-';
104
+ const pid = running ? parseInt(pidStr, 10) : undefined;
105
+ return {
106
+ running,
107
+ enabled: true, // If it's listed, it's loaded
108
+ pid,
109
+ message: stdout
110
+ };
111
+ }
112
+ return {
113
+ running: false,
114
+ enabled: false,
115
+ message: 'Service not loaded'
116
+ };
117
+ }
118
+ catch (error) {
119
+ // Service might not be loaded
120
+ return {
121
+ running: false,
122
+ enabled: false,
123
+ message: error instanceof Error ? error.message : String(error)
124
+ };
125
+ }
126
+ }
127
+ async logs(follow, lines = 100) {
128
+ const stdoutLog = path.join(this.logPath, 'stdout.log');
129
+ const stderrLog = path.join(this.logPath, 'stderr.log');
130
+ if (follow) {
131
+ // Use tail -f for following logs
132
+ const { spawn } = require('child_process');
133
+ console.log('==> stdout.log <==');
134
+ const stdoutProc = spawn('tail', ['-f', '-n', String(lines), stdoutLog], {
135
+ stdio: 'inherit'
136
+ });
137
+ console.log('\n==> stderr.log <==');
138
+ const stderrProc = spawn('tail', ['-f', '-n', String(lines), stderrLog], {
139
+ stdio: 'inherit'
140
+ });
141
+ // Handle process termination
142
+ process.on('SIGINT', () => {
143
+ stdoutProc.kill('SIGTERM');
144
+ stderrProc.kill('SIGTERM');
145
+ process.exit(0);
146
+ });
147
+ // Return empty string for follow mode as logs are streamed to stdout
148
+ return '';
149
+ }
150
+ else {
151
+ try {
152
+ let combinedLogs = '';
153
+ try {
154
+ const { stdout: stdoutContent } = await execAsync(`tail -n ${lines} ${stdoutLog}`);
155
+ combinedLogs += '==> stdout.log <==\n' + stdoutContent;
156
+ }
157
+ catch (error) {
158
+ // File might not exist yet
159
+ combinedLogs += '==> stdout.log <==\n(no logs yet)\n';
160
+ }
161
+ try {
162
+ const { stdout: stderrContent } = await execAsync(`tail -n ${lines} ${stderrLog}`);
163
+ combinedLogs += '\n==> stderr.log <==\n' + stderrContent;
164
+ }
165
+ catch (error) {
166
+ // File might not exist yet
167
+ combinedLogs += '\n==> stderr.log <==\n(no logs yet)\n';
168
+ }
169
+ return combinedLogs;
170
+ }
171
+ catch (error) {
172
+ throw new Error(`Failed to read logs: ${error instanceof Error ? error.message : String(error)}`);
173
+ }
174
+ }
175
+ }
176
+ async enable() {
177
+ // On launchd, loading the plist enables it
178
+ // It's already enabled by the install() method
179
+ try {
180
+ await execAsync(`launchctl load ${this.plistPath}`);
181
+ }
182
+ catch (error) {
183
+ // May already be loaded
184
+ }
185
+ }
186
+ async disable() {
187
+ try {
188
+ await execAsync(`launchctl unload ${this.plistPath}`);
189
+ }
190
+ catch (error) {
191
+ throw new Error(`Failed to disable service: ${error instanceof Error ? error.message : String(error)}`);
192
+ }
193
+ }
194
+ async uninstall() {
195
+ // Stop and unload service first
196
+ try {
197
+ await this.stop();
198
+ }
199
+ catch {
200
+ // Ignore if already stopped
201
+ }
202
+ // Remove plist file
203
+ try {
204
+ await fs.unlink(this.plistPath);
205
+ }
206
+ catch (error) {
207
+ throw new Error(`Failed to remove plist file: ${error instanceof Error ? error.message : String(error)}`);
208
+ }
209
+ }
210
+ getPlatform() {
211
+ return 'darwin';
212
+ }
213
+ getType() {
214
+ return 'launchd';
215
+ }
216
+ generatePlistFile(config) {
217
+ const nodePath = process.execPath; // Path to current Node.js executable
218
+ return `<?xml version="1.0" encoding="UTF-8"?>
219
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
220
+ <plist version="1.0">
221
+ <dict>
222
+ <key>Label</key>
223
+ <string>${this.serviceName}</string>
224
+ <key>ProgramArguments</key>
225
+ <array>
226
+ <string>${nodePath}</string>
227
+ <string>${config.agentPath}/index.js</string>
228
+ <string>start</string>
229
+ <string>-c</string>
230
+ <string>${config.configPath}/agent.config.json</string>
231
+ </array>
232
+ <key>RunAtLoad</key>
233
+ <true/>
234
+ <key>KeepAlive</key>
235
+ <true/>
236
+ <key>StandardOutPath</key>
237
+ <string>${this.logPath}/stdout.log</string>
238
+ <key>StandardErrorPath</key>
239
+ <string>${this.logPath}/stderr.log</string>
240
+ <key>EnvironmentVariables</key>
241
+ <dict>
242
+ <key>PATH</key>
243
+ <string>/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin</string>
244
+ <key>NODE_ENV</key>
245
+ <string>production</string>
246
+ <key>EDGIBLE_CONFIG_PATH</key>
247
+ <string>${config.configPath}</string>
248
+ </dict>
249
+ <key>WorkingDirectory</key>
250
+ <string>${config.agentPath}</string>
251
+ </dict>
252
+ </plist>
253
+ `;
254
+ }
255
+ }
256
+ exports.LaunchdDaemonManager = LaunchdDaemonManager;
257
+ //# sourceMappingURL=LaunchdDaemonManager.js.map
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PodmanDaemonManager = void 0;
37
+ const child_process_1 = require("child_process");
38
+ const util_1 = require("util");
39
+ const fs = __importStar(require("fs/promises"));
40
+ const path = __importStar(require("path"));
41
+ const os = __importStar(require("os"));
42
+ const PlatformDetector_1 = require("../../utils/PlatformDetector");
43
+ const PathResolver_1 = require("../../utils/PathResolver");
44
+ const execAsync = (0, util_1.promisify)(child_process_1.exec);
45
+ class PodmanDaemonManager {
46
+ constructor() {
47
+ this.containerName = 'edgible-agent';
48
+ this.imageName = 'edgible-agent:latest';
49
+ this.platform = PlatformDetector_1.PlatformDetector.getPlatform();
50
+ this.systemdUserPath = path.join(os.homedir(), '.config', 'systemd', 'user');
51
+ }
52
+ async install(config) {
53
+ // Check if image exists locally
54
+ const imageExists = await this.checkImageExists();
55
+ if (!imageExists) {
56
+ throw new Error(`Podman image '${this.imageName}' not found. Please build or pull the image first.\n` +
57
+ ` To build: cd agent-v2 && podman build -t ${this.imageName} .\n` +
58
+ ` Or pull from registry if available.`);
59
+ }
60
+ // Create Podman container with proper mounts and capabilities
61
+ const podmanRunCommand = this.generatePodmanRunCommand(config);
62
+ try {
63
+ // Remove existing container if it exists
64
+ try {
65
+ await execAsync(`podman rm -f ${this.containerName}`);
66
+ }
67
+ catch {
68
+ // Container doesn't exist, that's fine
69
+ }
70
+ // Create container
71
+ await execAsync(podmanRunCommand);
72
+ console.log('Podman container created successfully');
73
+ // Create systemd user service for auto-start
74
+ await this.createUserService(config);
75
+ }
76
+ catch (error) {
77
+ throw new Error(`Failed to create Podman container: ${error instanceof Error ? error.message : String(error)}`);
78
+ }
79
+ }
80
+ async start() {
81
+ // Use systemd user service if on Linux, otherwise direct podman command
82
+ if (this.platform === 'linux') {
83
+ try {
84
+ await execAsync(`systemctl --user start edgible-agent-podman`);
85
+ }
86
+ catch (error) {
87
+ // Fallback to direct podman command
88
+ await execAsync(`podman start ${this.containerName}`);
89
+ }
90
+ }
91
+ else {
92
+ await execAsync(`podman start ${this.containerName}`);
93
+ }
94
+ }
95
+ async stop() {
96
+ if (this.platform === 'linux') {
97
+ try {
98
+ await execAsync(`systemctl --user stop edgible-agent-podman`);
99
+ }
100
+ catch (error) {
101
+ // Fallback to direct podman command
102
+ await execAsync(`podman stop ${this.containerName}`);
103
+ }
104
+ }
105
+ else {
106
+ await execAsync(`podman stop ${this.containerName}`);
107
+ }
108
+ }
109
+ async restart() {
110
+ if (this.platform === 'linux') {
111
+ try {
112
+ await execAsync(`systemctl --user restart edgible-agent-podman`);
113
+ }
114
+ catch (error) {
115
+ // Fallback to direct podman command
116
+ await execAsync(`podman restart ${this.containerName}`);
117
+ }
118
+ }
119
+ else {
120
+ await execAsync(`podman restart ${this.containerName}`);
121
+ }
122
+ }
123
+ async status() {
124
+ try {
125
+ const { stdout } = await execAsync(`podman ps -a --filter name=${this.containerName} --format "{{.Status}}|||{{.ID}}"`);
126
+ if (!stdout.trim()) {
127
+ return {
128
+ running: false,
129
+ enabled: false,
130
+ message: 'Container not found'
131
+ };
132
+ }
133
+ const [status, containerId] = stdout.trim().split('|||');
134
+ const isRunning = status.toLowerCase().includes('up');
135
+ // Get more detailed info if running
136
+ if (isRunning) {
137
+ try {
138
+ const { stdout: inspectOut } = await execAsync(`podman inspect ${containerId} --format "{{.State.Pid}}"`);
139
+ const pid = parseInt(inspectOut.trim(), 10);
140
+ return {
141
+ running: true,
142
+ enabled: true,
143
+ pid: pid || undefined,
144
+ message: status
145
+ };
146
+ }
147
+ catch {
148
+ return {
149
+ running: true,
150
+ enabled: true,
151
+ message: status
152
+ };
153
+ }
154
+ }
155
+ return {
156
+ running: false,
157
+ enabled: true,
158
+ message: status
159
+ };
160
+ }
161
+ catch (error) {
162
+ return {
163
+ running: false,
164
+ enabled: false,
165
+ message: error instanceof Error ? error.message : String(error)
166
+ };
167
+ }
168
+ }
169
+ async logs(follow, lines = 100) {
170
+ if (follow) {
171
+ const { spawn } = require('child_process');
172
+ const proc = spawn('podman', ['logs', this.containerName, '--tail', String(lines), '-f'], {
173
+ stdio: 'inherit'
174
+ });
175
+ process.on('SIGINT', () => {
176
+ proc.kill('SIGTERM');
177
+ process.exit(0);
178
+ });
179
+ // Return empty string for follow mode as logs are streamed to stdout
180
+ return '';
181
+ }
182
+ else {
183
+ try {
184
+ const { stdout } = await execAsync(`podman logs ${this.containerName} --tail ${lines}`);
185
+ return stdout;
186
+ }
187
+ catch (error) {
188
+ throw new Error(`Failed to read logs: ${error instanceof Error ? error.message : String(error)}`);
189
+ }
190
+ }
191
+ }
192
+ async enable() {
193
+ if (this.platform === 'linux') {
194
+ try {
195
+ await execAsync('systemctl --user enable edgible-agent-podman');
196
+ // Enable lingering to allow user services to run without login
197
+ await execAsync('loginctl enable-linger');
198
+ }
199
+ catch (error) {
200
+ console.warn('Failed to enable systemd user service:', error);
201
+ }
202
+ }
203
+ // Other platforms don't have a built-in service manager for Podman
204
+ }
205
+ async disable() {
206
+ if (this.platform === 'linux') {
207
+ try {
208
+ await execAsync('systemctl --user disable edgible-agent-podman');
209
+ }
210
+ catch (error) {
211
+ console.warn('Failed to disable systemd user service:', error);
212
+ }
213
+ }
214
+ }
215
+ async uninstall() {
216
+ // Stop and remove container
217
+ try {
218
+ await this.stop();
219
+ }
220
+ catch {
221
+ // Ignore if already stopped
222
+ }
223
+ try {
224
+ await execAsync(`podman rm ${this.containerName}`);
225
+ }
226
+ catch (error) {
227
+ throw new Error(`Failed to remove container: ${error instanceof Error ? error.message : String(error)}`);
228
+ }
229
+ // Remove user service
230
+ if (this.platform === 'linux') {
231
+ try {
232
+ await this.disable();
233
+ await fs.unlink(path.join(this.systemdUserPath, 'edgible-agent-podman.service'));
234
+ await execAsync('systemctl --user daemon-reload');
235
+ }
236
+ catch {
237
+ // Ignore errors
238
+ }
239
+ }
240
+ }
241
+ getPlatform() {
242
+ return this.platform;
243
+ }
244
+ getType() {
245
+ return 'podman';
246
+ }
247
+ async checkImageExists() {
248
+ try {
249
+ await execAsync(`podman image inspect ${this.imageName}`);
250
+ return true;
251
+ }
252
+ catch {
253
+ return false;
254
+ }
255
+ }
256
+ async buildImage(agentPath) {
257
+ // Look for Dockerfile in agent-v2 directory
258
+ const dockerfilePath = path.join(path.dirname(path.dirname(path.dirname(agentPath))), 'agent-v2', 'Dockerfile');
259
+ const buildContext = path.dirname(dockerfilePath);
260
+ try {
261
+ console.log(`Building Podman image from ${buildContext}...`);
262
+ const { stdout, stderr } = await execAsync(`podman build -t ${this.imageName} -f ${dockerfilePath} ${buildContext}`, { maxBuffer: 10 * 1024 * 1024 } // 10MB buffer for build output
263
+ );
264
+ if (stdout)
265
+ console.log(stdout);
266
+ if (stderr)
267
+ console.error(stderr);
268
+ console.log('Podman image built successfully');
269
+ }
270
+ catch (error) {
271
+ throw new Error(`Failed to build Podman image: ${error instanceof Error ? error.message : String(error)}`);
272
+ }
273
+ }
274
+ generatePodmanRunCommand(config) {
275
+ const configPath = config.configPath;
276
+ const applicationsPath = path.join(configPath, 'applications');
277
+ const logsPath = path.join(configPath, 'logs');
278
+ // Ensure all required directories exist with proper permissions
279
+ try {
280
+ require('fs').mkdirSync(applicationsPath, { recursive: true, mode: 0o755 });
281
+ require('fs').mkdirSync(logsPath, { recursive: true, mode: 0o755 });
282
+ // Ensure parent directory also has proper permissions
283
+ require('fs').chmodSync(configPath, 0o755);
284
+ require('fs').chmodSync(applicationsPath, 0o755);
285
+ require('fs').chmodSync(logsPath, 0o755);
286
+ }
287
+ catch (error) {
288
+ console.warn('Could not set directory permissions:', error);
289
+ // Continue anyway - entrypoint will try to fix permissions
290
+ }
291
+ // Try to mount Docker socket from host (for Podman containers that need Docker)
292
+ // First check standard Docker socket location
293
+ let dockerSocketMount = '';
294
+ const dockerSocketPath = '/var/run/docker.sock';
295
+ if (require('fs').existsSync(dockerSocketPath)) {
296
+ dockerSocketMount = `-v "${dockerSocketPath}:${dockerSocketPath}"`;
297
+ }
298
+ // Podman socket path (for rootless mode) - optional
299
+ const podmanSocketPath = path.join(os.homedir(), '.local', 'share', 'containers', 'podman', 'machine');
300
+ let podmanSocketMount = '';
301
+ // Check if we need to mount the Podman socket
302
+ if (require('fs').existsSync(podmanSocketPath)) {
303
+ podmanSocketMount = `-v "${podmanSocketPath}:${podmanSocketPath}"`;
304
+ }
305
+ // For rootless Podman:
306
+ // - :z flag relabels SELinux context for shared container access
307
+ // - :U flag fixes ownership automatically (UID mapping)
308
+ // Both flags can be combined for systems with SELinux
309
+ // For serving devices, use bridge network to prevent exposing Caddy ports externally
310
+ // For gateway devices, use host network for direct network access
311
+ const networkMode = config.deviceType === 'serving' ? 'bridge' : 'host';
312
+ const networkFlag = networkMode === 'bridge' ? '--network bridge' : '--network host';
313
+ // For serving devices on bridge network, we don't expose ports
314
+ // Caddy will bind to localhost (127.0.0.1) only, so ports won't be accessible externally
315
+ // Use system path inside container
316
+ const containerConfigPath = PathResolver_1.PathResolver.getSystemDataPath() + '/agent';
317
+ const containerConfigFile = `${containerConfigPath}/agent.config.json`;
318
+ // Required capabilities:
319
+ // - NET_ADMIN: For WireGuard interface management, IP routing, iptables
320
+ // - NET_BIND_SERVICE: For Caddy to bind to privileged ports (80, 443)
321
+ // - SYS_MODULE: Optional, for kernel module loading (may not work in containers)
322
+ return `podman create \\
323
+ --name ${this.containerName} \\
324
+ ${networkFlag} \\
325
+ --cap-add NET_ADMIN \\
326
+ --cap-add NET_BIND_SERVICE \\
327
+ --restart unless-stopped \\
328
+ -v "${configPath}:${containerConfigPath}:z,U" \\
329
+ -v "${applicationsPath}:${containerConfigPath}/applications:z,U" \\
330
+ ${dockerSocketMount} \\
331
+ ${podmanSocketMount} \\
332
+ -e NODE_ENV=production \\
333
+ -e EDGIBLE_CONFIG_PATH="${containerConfigPath}" \\
334
+ -e DEVICE_TYPE=${config.deviceType} \\
335
+ ${this.imageName} \\
336
+ start --config "${containerConfigFile}"`;
337
+ }
338
+ async createUserService(config) {
339
+ if (this.platform !== 'linux') {
340
+ // Only create systemd user service on Linux
341
+ return;
342
+ }
343
+ const serviceContent = `[Unit]
344
+ Description=Edgible Agent (Podman)
345
+ After=network.target
346
+
347
+ [Service]
348
+ Type=oneshot
349
+ RemainAfterExit=yes
350
+ ExecStart=/usr/bin/podman start ${this.containerName}
351
+ ExecStop=/usr/bin/podman stop ${this.containerName}
352
+
353
+ [Install]
354
+ WantedBy=default.target
355
+ `;
356
+ try {
357
+ // Ensure systemd user directory exists
358
+ await fs.mkdir(this.systemdUserPath, { recursive: true });
359
+ await fs.writeFile(path.join(this.systemdUserPath, 'edgible-agent-podman.service'), serviceContent, 'utf8');
360
+ await execAsync('systemctl --user daemon-reload');
361
+ console.log('Systemd user service created');
362
+ }
363
+ catch (error) {
364
+ console.warn('Failed to create systemd user service:', error);
365
+ }
366
+ }
367
+ }
368
+ exports.PodmanDaemonManager = PodmanDaemonManager;
369
+ //# sourceMappingURL=PodmanDaemonManager.js.map