@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.
- package/LICENSE +136 -0
- package/README.md +450 -0
- package/dist/client/api-client.js +1057 -0
- package/dist/client/index.js +21 -0
- package/dist/commands/agent.js +1280 -0
- package/dist/commands/ai.js +608 -0
- package/dist/commands/application.js +885 -0
- package/dist/commands/auth.js +570 -0
- package/dist/commands/base/BaseCommand.js +93 -0
- package/dist/commands/base/CommandHandler.js +7 -0
- package/dist/commands/base/command-wrapper.js +58 -0
- package/dist/commands/base/middleware.js +77 -0
- package/dist/commands/config.js +116 -0
- package/dist/commands/connectivity.js +59 -0
- package/dist/commands/debug.js +98 -0
- package/dist/commands/discover.js +144 -0
- package/dist/commands/examples/migrated-command-example.js +180 -0
- package/dist/commands/gateway.js +494 -0
- package/dist/commands/managedGateway.js +787 -0
- package/dist/commands/utils/config-validator.js +76 -0
- package/dist/commands/utils/gateway-prompt.js +79 -0
- package/dist/commands/utils/input-parser.js +120 -0
- package/dist/commands/utils/output-formatter.js +109 -0
- package/dist/config/app-config.js +99 -0
- package/dist/detection/SystemCapabilityDetector.js +1244 -0
- package/dist/detection/ToolDetector.js +305 -0
- package/dist/detection/WorkloadDetector.js +314 -0
- package/dist/di/bindings.js +99 -0
- package/dist/di/container.js +88 -0
- package/dist/di/types.js +32 -0
- package/dist/index.js +52 -0
- package/dist/interfaces/IDaemonManager.js +3 -0
- package/dist/repositories/config-repository.js +62 -0
- package/dist/repositories/gateway-repository.js +35 -0
- package/dist/scripts/postinstall.js +101 -0
- package/dist/services/AgentStatusManager.js +299 -0
- package/dist/services/ConnectivityTester.js +271 -0
- package/dist/services/DependencyInstaller.js +475 -0
- package/dist/services/LocalAgentManager.js +2216 -0
- package/dist/services/application/ApplicationService.js +299 -0
- package/dist/services/auth/AuthService.js +214 -0
- package/dist/services/aws.js +644 -0
- package/dist/services/daemon/DaemonManagerFactory.js +65 -0
- package/dist/services/daemon/DockerDaemonManager.js +395 -0
- package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
- package/dist/services/daemon/PodmanDaemonManager.js +369 -0
- package/dist/services/daemon/SystemdDaemonManager.js +221 -0
- package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
- package/dist/services/daemon/index.js +16 -0
- package/dist/services/edgible.js +3060 -0
- package/dist/services/gateway/GatewayService.js +334 -0
- package/dist/state/config.js +146 -0
- package/dist/types/AgentConfig.js +5 -0
- package/dist/types/AgentStatus.js +5 -0
- package/dist/types/ApiClient.js +5 -0
- package/dist/types/ApiRequests.js +5 -0
- package/dist/types/ApiResponses.js +5 -0
- package/dist/types/Application.js +5 -0
- package/dist/types/CaddyJson.js +5 -0
- package/dist/types/UnifiedAgentStatus.js +56 -0
- package/dist/types/WireGuard.js +5 -0
- package/dist/types/Workload.js +5 -0
- package/dist/types/agent.js +5 -0
- package/dist/types/command-options.js +5 -0
- package/dist/types/connectivity.js +5 -0
- package/dist/types/errors.js +250 -0
- package/dist/types/gateway-types.js +5 -0
- package/dist/types/index.js +48 -0
- package/dist/types/models/ApplicationData.js +5 -0
- package/dist/types/models/CertificateData.js +5 -0
- package/dist/types/models/DeviceData.js +5 -0
- package/dist/types/models/DevicePoolData.js +5 -0
- package/dist/types/models/OrganizationData.js +5 -0
- package/dist/types/models/OrganizationInviteData.js +5 -0
- package/dist/types/models/ProviderConfiguration.js +5 -0
- package/dist/types/models/ResourceData.js +5 -0
- package/dist/types/models/ServiceResourceData.js +5 -0
- package/dist/types/models/UserData.js +5 -0
- package/dist/types/route.js +5 -0
- package/dist/types/validation/schemas.js +218 -0
- package/dist/types/validation.js +5 -0
- package/dist/utils/FileIntegrityManager.js +256 -0
- package/dist/utils/PathMigration.js +219 -0
- package/dist/utils/PathResolver.js +235 -0
- package/dist/utils/PlatformDetector.js +277 -0
- package/dist/utils/console-logger.js +130 -0
- package/dist/utils/docker-compose-parser.js +179 -0
- package/dist/utils/errors.js +130 -0
- package/dist/utils/health-checker.js +155 -0
- package/dist/utils/json-logger.js +72 -0
- package/dist/utils/log-formatter.js +293 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/network-utils.js +217 -0
- package/dist/utils/output.js +182 -0
- package/dist/utils/passwordValidation.js +91 -0
- package/dist/utils/progress.js +167 -0
- package/dist/utils/sudo-checker.js +22 -0
- package/dist/utils/urls.js +32 -0
- package/dist/utils/validation.js +31 -0
- package/dist/validation/schemas.js +175 -0
- package/dist/validation/validator.js +67 -0
- 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
|