@devskeo/ward-agent 1.0.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 (105) hide show
  1. package/dist/cli/config.d.ts +2 -0
  2. package/dist/cli/config.d.ts.map +1 -0
  3. package/dist/cli/config.js +30 -0
  4. package/dist/cli/config.js.map +1 -0
  5. package/dist/cli/pipe.d.ts +2 -0
  6. package/dist/cli/pipe.d.ts.map +1 -0
  7. package/dist/cli/pipe.js +53 -0
  8. package/dist/cli/pipe.js.map +1 -0
  9. package/dist/cli/service.d.ts +10 -0
  10. package/dist/cli/service.d.ts.map +1 -0
  11. package/dist/cli/service.js +134 -0
  12. package/dist/cli/service.js.map +1 -0
  13. package/dist/cli/start.d.ts +6 -0
  14. package/dist/cli/start.d.ts.map +1 -0
  15. package/dist/cli/start.js +160 -0
  16. package/dist/cli/start.js.map +1 -0
  17. package/dist/cli/status.d.ts +2 -0
  18. package/dist/cli/status.d.ts.map +1 -0
  19. package/dist/cli/status.js +56 -0
  20. package/dist/cli/status.js.map +1 -0
  21. package/dist/cli/stop.d.ts +2 -0
  22. package/dist/cli/stop.d.ts.map +1 -0
  23. package/dist/cli/stop.js +106 -0
  24. package/dist/cli/stop.js.map +1 -0
  25. package/dist/cli/systemd.d.ts +3 -0
  26. package/dist/cli/systemd.d.ts.map +1 -0
  27. package/dist/cli/systemd.js +84 -0
  28. package/dist/cli/systemd.js.map +1 -0
  29. package/dist/config/AgentConfig.d.ts +30 -0
  30. package/dist/config/AgentConfig.d.ts.map +1 -0
  31. package/dist/config/AgentConfig.js +155 -0
  32. package/dist/config/AgentConfig.js.map +1 -0
  33. package/dist/config/ServiceConfig.d.ts +30 -0
  34. package/dist/config/ServiceConfig.d.ts.map +1 -0
  35. package/dist/config/ServiceConfig.js +4 -0
  36. package/dist/config/ServiceConfig.js.map +1 -0
  37. package/dist/daemon.d.ts +2 -0
  38. package/dist/daemon.d.ts.map +1 -0
  39. package/dist/daemon.js +300 -0
  40. package/dist/daemon.js.map +1 -0
  41. package/dist/index.d.ts +3 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +94 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/logs/LogForwarder.d.ts +22 -0
  46. package/dist/logs/LogForwarder.d.ts.map +1 -0
  47. package/dist/logs/LogForwarder.js +83 -0
  48. package/dist/logs/LogForwarder.js.map +1 -0
  49. package/dist/logs/LogWatcher.d.ts +15 -0
  50. package/dist/logs/LogWatcher.d.ts.map +1 -0
  51. package/dist/logs/LogWatcher.js +111 -0
  52. package/dist/logs/LogWatcher.js.map +1 -0
  53. package/dist/logs/ServiceWatcher.d.ts +32 -0
  54. package/dist/logs/ServiceWatcher.d.ts.map +1 -0
  55. package/dist/logs/ServiceWatcher.js +294 -0
  56. package/dist/logs/ServiceWatcher.js.map +1 -0
  57. package/dist/metrics/CpuCollector.d.ts +9 -0
  58. package/dist/metrics/CpuCollector.d.ts.map +1 -0
  59. package/dist/metrics/CpuCollector.js +24 -0
  60. package/dist/metrics/CpuCollector.js.map +1 -0
  61. package/dist/metrics/DiskCollector.d.ts +11 -0
  62. package/dist/metrics/DiskCollector.d.ts.map +1 -0
  63. package/dist/metrics/DiskCollector.js +51 -0
  64. package/dist/metrics/DiskCollector.js.map +1 -0
  65. package/dist/metrics/IpCollector.d.ts +13 -0
  66. package/dist/metrics/IpCollector.d.ts.map +1 -0
  67. package/dist/metrics/IpCollector.js +34 -0
  68. package/dist/metrics/IpCollector.js.map +1 -0
  69. package/dist/metrics/MemoryCollector.d.ts +12 -0
  70. package/dist/metrics/MemoryCollector.d.ts.map +1 -0
  71. package/dist/metrics/MemoryCollector.js +27 -0
  72. package/dist/metrics/MemoryCollector.js.map +1 -0
  73. package/dist/metrics/NetworkCollector.d.ts +9 -0
  74. package/dist/metrics/NetworkCollector.d.ts.map +1 -0
  75. package/dist/metrics/NetworkCollector.js +24 -0
  76. package/dist/metrics/NetworkCollector.js.map +1 -0
  77. package/dist/metrics/ProcessCollector.d.ts +16 -0
  78. package/dist/metrics/ProcessCollector.d.ts.map +1 -0
  79. package/dist/metrics/ProcessCollector.js +34 -0
  80. package/dist/metrics/ProcessCollector.js.map +1 -0
  81. package/dist/start-dev.d.ts +2 -0
  82. package/dist/start-dev.d.ts.map +1 -0
  83. package/dist/start-dev.js +205 -0
  84. package/dist/start-dev.js.map +1 -0
  85. package/dist/start-self.d.ts +2 -0
  86. package/dist/start-self.d.ts.map +1 -0
  87. package/dist/start-self.js +271 -0
  88. package/dist/start-self.js.map +1 -0
  89. package/dist/transport/HttpClient.d.ts +53 -0
  90. package/dist/transport/HttpClient.d.ts.map +1 -0
  91. package/dist/transport/HttpClient.js +172 -0
  92. package/dist/transport/HttpClient.js.map +1 -0
  93. package/dist/transport/Queue.d.ts +29 -0
  94. package/dist/transport/Queue.d.ts.map +1 -0
  95. package/dist/transport/Queue.js +74 -0
  96. package/dist/transport/Queue.js.map +1 -0
  97. package/dist/transport/ReconnectManager.d.ts +14 -0
  98. package/dist/transport/ReconnectManager.d.ts.map +1 -0
  99. package/dist/transport/ReconnectManager.js +59 -0
  100. package/dist/transport/ReconnectManager.js.map +1 -0
  101. package/dist/types/index.d.ts +35 -0
  102. package/dist/types/index.d.ts.map +1 -0
  103. package/dist/types/index.js +3 -0
  104. package/dist/types/index.js.map +1 -0
  105. package/package.json +41 -0
package/dist/index.js ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ // Ward 에이전트 CLI 진입점
5
+ const commander_1 = require("commander");
6
+ const start_js_1 = require("./cli/start.js");
7
+ const stop_js_1 = require("./cli/stop.js");
8
+ const status_js_1 = require("./cli/status.js");
9
+ const config_js_1 = require("./cli/config.js");
10
+ const service_js_1 = require("./cli/service.js");
11
+ const pipe_js_1 = require("./cli/pipe.js");
12
+ const AgentConfig_js_1 = require("./config/AgentConfig.js");
13
+ const program = new commander_1.Command();
14
+ program
15
+ .name('ward')
16
+ .description('Ward 서버 모니터링 에이전트')
17
+ .version('0.1.0');
18
+ // ward start [serverUrl] - 에이전트 시작
19
+ program
20
+ .command('start [serverUrl]')
21
+ .description('에이전트를 시작합니다. 처음 실행 시 서버 URL을 지정하세요.')
22
+ .option('--name <groupName>', '서버 그룹명 (클러스터 환경에서 서버를 그룹화)')
23
+ .action(async (serverUrl, options) => {
24
+ if (!serverUrl) {
25
+ // 기존 config에서 URL 로드
26
+ const config = (0, AgentConfig_js_1.loadConfig)();
27
+ if (!config?.server?.url) {
28
+ console.error('서버 URL이 필요합니다. 예: ward start https://ward.example.com');
29
+ process.exit(1);
30
+ }
31
+ serverUrl = config.server.url;
32
+ }
33
+ await (0, start_js_1.start)(serverUrl, options ?? {});
34
+ });
35
+ // ward stop - 에이전트 중지
36
+ program
37
+ .command('stop')
38
+ .description('에이전트를 중지하고 서버에서 등록을 해제합니다')
39
+ .action(async () => {
40
+ await (0, stop_js_1.stop)();
41
+ });
42
+ // ward status - 에이전트 상태 확인
43
+ program
44
+ .command('status')
45
+ .description('에이전트 실행 상태를 확인합니다')
46
+ .action(() => {
47
+ (0, status_js_1.status)();
48
+ });
49
+ // ward config - 설정 관리
50
+ const configCmd = program
51
+ .command('config')
52
+ .description('에이전트 설정을 관리합니다');
53
+ configCmd
54
+ .command('show')
55
+ .description('현재 설정을 출력합니다')
56
+ .action(() => {
57
+ (0, config_js_1.configShow)();
58
+ });
59
+ // ward service - 로그 수집 서비스 관리
60
+ const serviceCmd = program
61
+ .command('service')
62
+ .description('로그 수집 서비스 관리');
63
+ serviceCmd
64
+ .command('add <name>')
65
+ .description('로그 수집 서비스 등록')
66
+ .option('--log <path>', '감시할 로그 파일 경로 (여러 번 사용 가능)', (v, prev) => [...(prev ?? []), v], [])
67
+ .option('--exec <command>', '실행할 명령어 (stdout/stderr 수집)')
68
+ .option('--cwd <dir>', '명령어 실행 디렉토리 (--exec와 함께 사용)')
69
+ .option('--journal <unit>', 'systemd 유닛 이름 (예: nginx.service)')
70
+ .option('--docker <container>', '도커 컨테이너 이름')
71
+ .action(async (name, options) => {
72
+ await (0, service_js_1.serviceAdd)(name, options);
73
+ });
74
+ serviceCmd
75
+ .command('remove <name>')
76
+ .description('서비스 제거')
77
+ .action(async (name) => {
78
+ await (0, service_js_1.serviceRemove)(name);
79
+ });
80
+ serviceCmd
81
+ .command('list')
82
+ .description('등록된 서비스 목록')
83
+ .action(() => {
84
+ (0, service_js_1.serviceList)();
85
+ });
86
+ // ward pipe <name> - stdin을 로그로 전송
87
+ program
88
+ .command('pipe <serviceName>')
89
+ .description('stdin을 지정한 서비스 이름으로 로그 전송\n예: node app.js 2>&1 | ward pipe my-api')
90
+ .action(async (serviceName) => {
91
+ await (0, pipe_js_1.pipe)(serviceName);
92
+ });
93
+ program.parse(process.argv);
94
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { HttpClient } from '../transport/HttpClient.js';
2
+ export interface LogForwarderOptions {
3
+ client: HttpClient;
4
+ batchSize?: number;
5
+ flushIntervalMs?: number;
6
+ }
7
+ export declare class LogForwarder {
8
+ private readonly client;
9
+ private readonly batchSize;
10
+ private readonly flushIntervalMs;
11
+ private readonly queue;
12
+ private buffer;
13
+ private flushTimer;
14
+ constructor(options: LogForwarderOptions);
15
+ start(): void;
16
+ stop(): Promise<void>;
17
+ addLog(source: string, line: string, level?: string): void;
18
+ get bufferSize(): number;
19
+ private _flush;
20
+ private _retryQueue;
21
+ }
22
+ //# sourceMappingURL=LogForwarder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogForwarder.d.ts","sourceRoot":"","sources":["../../src/logs/LogForwarder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAIxD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAGD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,UAAU,CAA+C;gBAErD,OAAO,EAAE,mBAAmB;IAQxC,KAAK,IAAI,IAAI;IASP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI;IAiB1D,IAAI,UAAU,IAAI,MAAM,CAEvB;YAGa,MAAM;YAkBN,WAAW;CAY1B"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LogForwarder = void 0;
4
+ const Queue_js_1 = require("../transport/Queue.js");
5
+ // 로그 라인을 버퍼에 쌓다가 배치로 서버에 전송
6
+ class LogForwarder {
7
+ client;
8
+ batchSize;
9
+ flushIntervalMs;
10
+ queue;
11
+ buffer = [];
12
+ flushTimer = null;
13
+ constructor(options) {
14
+ this.client = options.client;
15
+ this.batchSize = options.batchSize ?? 100;
16
+ this.flushIntervalMs = options.flushIntervalMs ?? 5000;
17
+ this.queue = new Queue_js_1.Queue({ maxSize: 10000, maxRetries: 3 });
18
+ }
19
+ // 포워더 시작 (주기적 플러시 타이머 등록)
20
+ start() {
21
+ if (this.flushTimer)
22
+ return;
23
+ this.flushTimer = setInterval(() => {
24
+ void this._flush();
25
+ }, this.flushIntervalMs);
26
+ }
27
+ // 포워더 중단
28
+ async stop() {
29
+ if (this.flushTimer) {
30
+ clearInterval(this.flushTimer);
31
+ this.flushTimer = null;
32
+ }
33
+ // 남은 버퍼 전송 시도
34
+ await this._flush();
35
+ }
36
+ // 로그 라인 추가
37
+ addLog(source, line, level = 'info') {
38
+ const entry = {
39
+ source,
40
+ level,
41
+ message: line,
42
+ loggedAt: new Date().toISOString(),
43
+ };
44
+ this.buffer.push(entry);
45
+ // 배치 크기 도달 시 즉시 전송
46
+ if (this.buffer.length >= this.batchSize) {
47
+ void this._flush();
48
+ }
49
+ }
50
+ // 현재 버퍼 크기 (테스트용)
51
+ get bufferSize() {
52
+ return this.buffer.length;
53
+ }
54
+ // 내부: 버퍼를 서버로 전송
55
+ async _flush() {
56
+ if (this.buffer.length === 0)
57
+ return;
58
+ const batch = this.buffer.splice(0, this.batchSize);
59
+ // 먼저 큐에 쌓인 실패 항목 재전송 시도
60
+ await this._retryQueue();
61
+ const result = await this.client.post('/api/agent/logs', { logs: batch });
62
+ if (!result.success) {
63
+ // 전송 실패 시 큐에 버퍼링
64
+ this.queue.enqueue('/api/agent/logs', { logs: batch });
65
+ console.error('[LogForwarder] 로그 전송 실패, 큐에 저장:', result.error);
66
+ }
67
+ }
68
+ // 내부: 큐에 쌓인 항목 재전송
69
+ async _retryQueue() {
70
+ if (this.queue.isEmpty)
71
+ return;
72
+ const items = this.queue.dequeueAll();
73
+ for (const item of items) {
74
+ const result = await this.client.post(item.path, item.data);
75
+ if (!result.success) {
76
+ // 재시도 횟수 초과 시 폐기
77
+ this.queue.requeueItem(item);
78
+ }
79
+ }
80
+ }
81
+ }
82
+ exports.LogForwarder = LogForwarder;
83
+ //# sourceMappingURL=LogForwarder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogForwarder.js","sourceRoot":"","sources":["../../src/logs/LogForwarder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { EventEmitter } from 'events';
2
+ export interface LogWatcherEvents {
3
+ line: (source: string, line: string) => void;
4
+ error: (err: Error) => void;
5
+ }
6
+ export declare class LogWatcher extends EventEmitter {
7
+ private watchedFiles;
8
+ watch(filePath: string, source: string): void;
9
+ unwatch(filePath: string): void;
10
+ unwatchAll(): void;
11
+ private _startWatcher;
12
+ private _handleFileChange;
13
+ getWatchedSources(): string[];
14
+ }
15
+ //# sourceMappingURL=LogWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogWatcher.d.ts","sourceRoot":"","sources":["../../src/logs/LogWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAC7B;AAWD,qBAAa,UAAW,SAAQ,YAAY;IAC1C,OAAO,CAAC,YAAY,CAAuC;IAG3D,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IA4B7C,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAU/B,UAAU,IAAI,IAAI;IAQlB,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,iBAAiB;IAwCzB,iBAAiB,IAAI,MAAM,EAAE;CAG9B"}
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LogWatcher = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const events_1 = require("events");
10
+ // fs.watch + 파일 포인터 방식으로 tail -f 구현
11
+ class LogWatcher extends events_1.EventEmitter {
12
+ watchedFiles = new Map();
13
+ // 로그 파일 감시 등록
14
+ watch(filePath, source) {
15
+ const absPath = path_1.default.resolve(filePath);
16
+ if (this.watchedFiles.has(absPath)) {
17
+ return; // 이미 감시 중
18
+ }
19
+ const entry = {
20
+ filePath: absPath,
21
+ source,
22
+ position: 0,
23
+ watcher: null,
24
+ };
25
+ // 파일이 존재하면 끝 위치부터 읽기 시작 (기존 내용 무시)
26
+ try {
27
+ const stat = fs_1.default.statSync(absPath);
28
+ entry.position = stat.size;
29
+ }
30
+ catch {
31
+ // 파일이 없으면 0부터 시작 (파일 생성 대기)
32
+ entry.position = 0;
33
+ }
34
+ this.watchedFiles.set(absPath, entry);
35
+ this._startWatcher(entry);
36
+ }
37
+ // 특정 파일 감시 중단
38
+ unwatch(filePath) {
39
+ const absPath = path_1.default.resolve(filePath);
40
+ const entry = this.watchedFiles.get(absPath);
41
+ if (entry) {
42
+ entry.watcher?.close();
43
+ this.watchedFiles.delete(absPath);
44
+ }
45
+ }
46
+ // 모든 감시 중단
47
+ unwatchAll() {
48
+ for (const [, entry] of this.watchedFiles) {
49
+ entry.watcher?.close();
50
+ }
51
+ this.watchedFiles.clear();
52
+ }
53
+ // 내부: fs.FSWatcher 시작
54
+ _startWatcher(entry) {
55
+ try {
56
+ const watcher = fs_1.default.watch(entry.filePath, { persistent: false }, (eventType) => {
57
+ if (eventType === 'change' || eventType === 'rename') {
58
+ this._handleFileChange(entry);
59
+ }
60
+ });
61
+ watcher.on('error', (err) => {
62
+ this.emit('error', err);
63
+ });
64
+ entry.watcher = watcher;
65
+ }
66
+ catch {
67
+ // 파일이 없으면 감시를 시작할 수 없음 — 정상 상황
68
+ entry.watcher = null;
69
+ }
70
+ }
71
+ // 내부: 파일 변경 처리
72
+ _handleFileChange(entry) {
73
+ try {
74
+ const stat = fs_1.default.statSync(entry.filePath);
75
+ // 파일 로테이션 감지: 파일 크기가 현재 포인터보다 작아지면 처음부터 다시 읽기
76
+ if (stat.size < entry.position) {
77
+ entry.position = 0;
78
+ }
79
+ if (stat.size <= entry.position) {
80
+ return; // 새 내용 없음
81
+ }
82
+ // 새로 추가된 부분만 읽기
83
+ const fd = fs_1.default.openSync(entry.filePath, 'r');
84
+ const length = stat.size - entry.position;
85
+ const buffer = Buffer.alloc(length);
86
+ fs_1.default.readSync(fd, buffer, 0, length, entry.position);
87
+ fs_1.default.closeSync(fd);
88
+ entry.position = stat.size;
89
+ // 라인 단위로 분리하여 이벤트 발생
90
+ const text = buffer.toString('utf8');
91
+ const lines = text.split('\n');
92
+ for (const line of lines) {
93
+ const trimmed = line.trimEnd();
94
+ if (trimmed.length > 0) {
95
+ this.emit('line', entry.source, trimmed);
96
+ }
97
+ }
98
+ }
99
+ catch (err) {
100
+ if (err instanceof Error) {
101
+ this.emit('error', err);
102
+ }
103
+ }
104
+ }
105
+ // 감시 중인 파일 목록 반환 (테스트용)
106
+ getWatchedSources() {
107
+ return Array.from(this.watchedFiles.values()).map((e) => e.source);
108
+ }
109
+ }
110
+ exports.LogWatcher = LogWatcher;
111
+ //# sourceMappingURL=LogWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogWatcher.js","sourceRoot":"","sources":["../../src/logs/LogWatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { EventEmitter } from 'events';
2
+ import type { ServiceConfig } from '../config/ServiceConfig.js';
3
+ /**
4
+ * file / exec / journal / docker / pipe 방식으로
5
+ * 서비스 로그를 수집해 'line' 이벤트를 발생시킨다.
6
+ */
7
+ export declare class ServiceWatcher extends EventEmitter {
8
+ private entries;
9
+ private lineBuffers;
10
+ watch(config: ServiceConfig): void;
11
+ unwatch(name: string): void;
12
+ unwatchAll(): void;
13
+ unwatchAllAndWait(timeoutMs?: number): Promise<void>;
14
+ getWatchedNames(): string[];
15
+ private _watchFile;
16
+ private _startFileWatcher;
17
+ private _handleFileChange;
18
+ private _watchExec;
19
+ private _spawnExec;
20
+ private _watchJournal;
21
+ private _watchDocker;
22
+ private _watchPipe;
23
+ restart(name: string): void;
24
+ getServiceStatus(name: string): {
25
+ status: 'running' | 'stopped' | 'unknown';
26
+ pid?: number;
27
+ restartCount: number;
28
+ startedAt?: Date;
29
+ };
30
+ private _emitLines;
31
+ }
32
+ //# sourceMappingURL=ServiceWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServiceWatcher.d.ts","sourceRoot":"","sources":["../../src/logs/ServiceWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AA0BhE;;;GAGG;AACH,qBAAa,cAAe,SAAQ,YAAY;IAC9C,OAAO,CAAC,OAAO,CAAsC;IACrD,OAAO,CAAC,WAAW,CAAkB;IAGrC,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAuBlC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAiC3B,UAAU,IAAI,IAAI;IAOZ,iBAAiB,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BxD,eAAe,IAAI,MAAM,EAAE;IAM3B,OAAO,CAAC,UAAU;IAqBlB,OAAO,CAAC,iBAAiB;IAczB,OAAO,CAAC,iBAAiB;IAuBzB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,UAAU;IAiDlB,OAAO,CAAC,aAAa;IAerB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAe3B,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,IAAI,CAAA;KAAE;IAkBnI,OAAO,CAAC,UAAU;CAenB"}