@kapeta/local-cluster-service 0.15.2 → 0.16.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 (36) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/src/RepositoryWatcher.d.ts +22 -0
  3. package/dist/cjs/src/RepositoryWatcher.js +273 -0
  4. package/dist/cjs/src/assetManager.js +33 -20
  5. package/dist/cjs/src/containerManager.js +0 -1
  6. package/dist/cjs/src/identities/routes.js +2 -1
  7. package/dist/cjs/src/instanceManager.d.ts +0 -2
  8. package/dist/cjs/src/instanceManager.js +16 -35
  9. package/dist/cjs/src/progressListener.d.ts +5 -6
  10. package/dist/cjs/src/progressListener.js +54 -36
  11. package/dist/cjs/src/repositoryManager.d.ts +4 -4
  12. package/dist/cjs/src/repositoryManager.js +20 -93
  13. package/dist/cjs/src/socketManager.d.ts +18 -6
  14. package/dist/cjs/src/socketManager.js +35 -1
  15. package/dist/esm/src/RepositoryWatcher.d.ts +22 -0
  16. package/dist/esm/src/RepositoryWatcher.js +266 -0
  17. package/dist/esm/src/assetManager.js +35 -22
  18. package/dist/esm/src/containerManager.js +0 -1
  19. package/dist/esm/src/identities/routes.js +2 -1
  20. package/dist/esm/src/instanceManager.d.ts +0 -2
  21. package/dist/esm/src/instanceManager.js +16 -35
  22. package/dist/esm/src/progressListener.d.ts +5 -6
  23. package/dist/esm/src/progressListener.js +53 -36
  24. package/dist/esm/src/repositoryManager.d.ts +4 -4
  25. package/dist/esm/src/repositoryManager.js +20 -93
  26. package/dist/esm/src/socketManager.d.ts +18 -6
  27. package/dist/esm/src/socketManager.js +34 -0
  28. package/package.json +2 -2
  29. package/src/RepositoryWatcher.ts +304 -0
  30. package/src/assetManager.ts +39 -25
  31. package/src/containerManager.ts +0 -5
  32. package/src/identities/routes.ts +2 -1
  33. package/src/instanceManager.ts +22 -35
  34. package/src/progressListener.ts +59 -39
  35. package/src/repositoryManager.ts +26 -100
  36. package/src/socketManager.ts +44 -5
@@ -0,0 +1,266 @@
1
+ import chokidar from 'chokidar';
2
+ import ClusterConfiguration from '@kapeta/local-cluster-config';
3
+ import FS from 'fs-extra';
4
+ import Path from 'node:path';
5
+ import YAML from 'yaml';
6
+ import { parseKapetaUri } from '@kapeta/nodejs-utils';
7
+ import _ from 'lodash';
8
+ import { socketManager } from './socketManager';
9
+ import { definitionsManager } from './definitionsManager';
10
+ import { assetManager } from './assetManager';
11
+ function clearAllCaches() {
12
+ definitionsManager.clearCache();
13
+ assetManager.clearCache();
14
+ }
15
+ const KAPETA_YML_RX = /^kapeta.ya?ml$/;
16
+ export class RepositoryWatcher {
17
+ watcher;
18
+ disabled = false;
19
+ baseDir;
20
+ allDefinitions = [];
21
+ symbolicLinks = {};
22
+ ignoredFiles = new Set();
23
+ constructor() {
24
+ this.baseDir = ClusterConfiguration.getRepositoryBasedir();
25
+ }
26
+ setDisabled(disabled) {
27
+ this.disabled = disabled;
28
+ }
29
+ watch() {
30
+ if (!FS.existsSync(this.baseDir)) {
31
+ FS.mkdirpSync(this.baseDir);
32
+ }
33
+ this.allDefinitions = ClusterConfiguration.getDefinitions();
34
+ try {
35
+ this.watcher = chokidar.watch(this.baseDir, {
36
+ followSymlinks: false,
37
+ ignorePermissionErrors: true,
38
+ disableGlobbing: true,
39
+ persistent: true,
40
+ depth: 2,
41
+ ignored: (path) => this.ignoreFile(path),
42
+ });
43
+ this.watcher.on('all', this.handleFileChange.bind(this));
44
+ this.watcher.on('error', (error) => {
45
+ console.log('Error watching repository', error);
46
+ });
47
+ this.watcher.on('ready', () => {
48
+ console.log('Watching local repository for provider changes: %s', this.baseDir);
49
+ });
50
+ }
51
+ catch (e) {
52
+ // Fallback to run without watch mode due to potential platform issues.
53
+ // https://nodejs.org/docs/latest/api/fs.html#caveats
54
+ console.log('Unable to watch for changes. Changes to assets will not update automatically.', e);
55
+ return;
56
+ }
57
+ }
58
+ async ignoreChangesFor(file) {
59
+ this.ignoredFiles.add(file);
60
+ const realPath = await FS.realpath(file);
61
+ if (realPath !== file) {
62
+ this.ignoredFiles.add(realPath);
63
+ }
64
+ }
65
+ async resumeChangedFor(file) {
66
+ this.ignoredFiles.delete(file);
67
+ const realPath = await FS.realpath(file);
68
+ if (realPath !== file) {
69
+ this.ignoredFiles.delete(realPath);
70
+ }
71
+ }
72
+ async unwatch() {
73
+ if (!this.watcher) {
74
+ return;
75
+ }
76
+ this.symbolicLinks = {};
77
+ await this.watcher.close();
78
+ this.watcher = undefined;
79
+ }
80
+ async getAssetIdentity(path) {
81
+ const baseName = Path.basename(path);
82
+ let handle, name, version;
83
+ if (path.startsWith(this.baseDir)) {
84
+ const relativePath = Path.relative(this.baseDir, path);
85
+ // Inside the repo we can use the path to determine the handle, name and version
86
+ [handle, name, version] = relativePath.split(/\//g);
87
+ if (!handle || !name || !version) {
88
+ // Do nothing with this
89
+ return;
90
+ }
91
+ return {
92
+ handle,
93
+ name,
94
+ version,
95
+ };
96
+ }
97
+ if (!KAPETA_YML_RX.test(baseName)) {
98
+ // Do nothing with this
99
+ return;
100
+ }
101
+ // Outside the repo we need to use the file content to determine the handle, name
102
+ // Version is always 'local'
103
+ version = 'local';
104
+ try {
105
+ const definition = YAML.parse((await FS.readFile(path)).toString());
106
+ const uri = parseKapetaUri(definition.metadata.name);
107
+ handle = uri.handle;
108
+ name = uri.name;
109
+ return {
110
+ handle,
111
+ name,
112
+ version,
113
+ };
114
+ }
115
+ catch (e) {
116
+ // Ignore issues in the YML file
117
+ return;
118
+ }
119
+ }
120
+ async handleFileChange(eventName, path) {
121
+ if (!path) {
122
+ return;
123
+ }
124
+ if (this.ignoredFiles.has(path)) {
125
+ return;
126
+ }
127
+ //console.log('File changed', eventName, path);
128
+ const assetIdentity = await this.getAssetIdentity(path);
129
+ if (!assetIdentity) {
130
+ return;
131
+ }
132
+ if (this.disabled) {
133
+ return;
134
+ }
135
+ // If this is false it's because we're watching a symlink target
136
+ const withinRepo = path.startsWith(this.baseDir);
137
+ if (withinRepo && assetIdentity.version === 'local' && path.endsWith('/local')) {
138
+ // This is likely a symlink target
139
+ if (eventName === 'add') {
140
+ //console.log('Checking if we should add symlink target', handle, name, version, path);
141
+ await this.addSymlinkTarget(path);
142
+ }
143
+ if (eventName === 'unlink') {
144
+ await this.removeSymlinkTarget(path);
145
+ }
146
+ if (eventName === 'change') {
147
+ await this.updateSymlinkTarget(path);
148
+ }
149
+ }
150
+ await this.checkForChange(assetIdentity);
151
+ }
152
+ async checkForChange(assetIdentity) {
153
+ const ymlPath = Path.join(this.baseDir, assetIdentity.handle, assetIdentity.name, assetIdentity.version, 'kapeta.yml');
154
+ const newDefinitions = ClusterConfiguration.getDefinitions();
155
+ const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
156
+ let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
157
+ const ymlExists = await this.exists(ymlPath);
158
+ let type;
159
+ if (ymlExists) {
160
+ if (currentDefinition) {
161
+ if (newDefinition && _.isEqual(currentDefinition, newDefinition)) {
162
+ //Definition was not changed
163
+ return;
164
+ }
165
+ type = 'updated';
166
+ }
167
+ else if (newDefinition) {
168
+ type = 'added';
169
+ currentDefinition = newDefinition;
170
+ }
171
+ else {
172
+ //Other definition was added / updated - ignore
173
+ return;
174
+ }
175
+ }
176
+ else {
177
+ if (currentDefinition) {
178
+ const ref = parseKapetaUri(`${currentDefinition.definition.metadata.name}:${currentDefinition.version}`).id;
179
+ //Something was removed
180
+ type = 'removed';
181
+ }
182
+ else {
183
+ //Other definition was removed - ignore
184
+ return;
185
+ }
186
+ }
187
+ const payload = {
188
+ type,
189
+ definition: newDefinition?.definition ?? currentDefinition?.definition,
190
+ asset: assetIdentity,
191
+ };
192
+ this.allDefinitions = newDefinitions;
193
+ //console.log('Asset changed', payload);
194
+ socketManager.emitGlobal('asset-change', payload);
195
+ clearAllCaches();
196
+ }
197
+ async exists(path) {
198
+ try {
199
+ await FS.access(path);
200
+ return true;
201
+ }
202
+ catch (e) {
203
+ return false;
204
+ }
205
+ }
206
+ async removeSymlinkTarget(path) {
207
+ if (this.symbolicLinks[path]) {
208
+ //console.log('Unwatching symlink target %s => %s', path, this.symbolicLinks[path]);
209
+ this.watcher?.unwatch(this.symbolicLinks[path]);
210
+ delete this.symbolicLinks[path];
211
+ }
212
+ }
213
+ async updateSymlinkTarget(path) {
214
+ if (this.symbolicLinks[path]) {
215
+ //console.log('Updating symlink target %s => %s', path, this.symbolicLinks[path]);
216
+ this.watcher?.unwatch(this.symbolicLinks[path]);
217
+ delete this.symbolicLinks[path];
218
+ await this.addSymlinkTarget(path);
219
+ }
220
+ }
221
+ async addSymlinkTarget(path) {
222
+ try {
223
+ // Make sure we're not watching the symlink target
224
+ await this.removeSymlinkTarget(path);
225
+ const stat = await FS.lstat(path);
226
+ if (stat.isSymbolicLink()) {
227
+ const realPath = `${await FS.realpath(path)}/kapeta.yml`;
228
+ if (await this.exists(realPath)) {
229
+ //console.log('Watching symlink target %s => %s', path, realPath);
230
+ this.watcher?.add(realPath);
231
+ this.symbolicLinks[path] = realPath;
232
+ }
233
+ }
234
+ }
235
+ catch (e) {
236
+ // Ignore
237
+ console.warn('Failed to check local symlink target', e);
238
+ }
239
+ }
240
+ ignoreFile(path) {
241
+ if (!path.startsWith(this.baseDir)) {
242
+ return false;
243
+ }
244
+ if (path.includes('/node_modules/')) {
245
+ return true;
246
+ }
247
+ const filename = Path.basename(path);
248
+ if (filename.startsWith('.')) {
249
+ return true;
250
+ }
251
+ const relativePath = Path.relative(this.baseDir, path).split(Path.sep);
252
+ try {
253
+ if (FS.statSync(path).isDirectory()) {
254
+ if (relativePath.length > 3) {
255
+ return true;
256
+ }
257
+ return false;
258
+ }
259
+ }
260
+ catch (e) {
261
+ // Didn't exist - dont ignore
262
+ return false;
263
+ }
264
+ return !/^kapeta\.ya?ml$/.test(filename);
265
+ }
266
+ }
@@ -1,16 +1,19 @@
1
1
  import Path from 'node:path';
2
- import FS from 'node:fs';
3
- import FSExtra from 'fs-extra';
2
+ import FS from 'fs-extra';
4
3
  import YAML from 'yaml';
5
4
  import NodeCache from 'node-cache';
6
5
  import { codeGeneratorManager } from './codeGeneratorManager';
7
- import { progressListener } from './progressListener';
6
+ import { ProgressListener } from './progressListener';
8
7
  import { parseKapetaUri } from '@kapeta/nodejs-utils';
9
8
  import { repositoryManager } from './repositoryManager';
10
9
  import { Actions } from '@kapeta/nodejs-registry-utils';
11
10
  import { definitionsManager } from './definitionsManager';
12
11
  import { normalizeKapetaUri } from './utils/utils';
13
12
  import { taskManager } from './taskManager';
13
+ function clearAllCaches() {
14
+ definitionsManager.clearCache();
15
+ assetManager.clearCache();
16
+ }
14
17
  function enrichAsset(asset) {
15
18
  return {
16
19
  ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
@@ -97,19 +100,16 @@ class AssetManager {
97
100
  return asset;
98
101
  }
99
102
  async createAsset(path, yaml) {
100
- if (FS.existsSync(path)) {
103
+ if (await FS.pathExists(path)) {
101
104
  throw new Error('File already exists: ' + path);
102
105
  }
103
106
  const dirName = Path.dirname(path);
104
- if (!FS.existsSync(dirName)) {
105
- FSExtra.mkdirpSync(dirName);
107
+ if (!(await FS.pathExists(dirName))) {
108
+ await FS.mkdirp(dirName);
106
109
  }
107
- console.log('Wrote to ' + path);
108
- FS.writeFileSync(path, YAML.stringify(yaml));
110
+ await FS.writeFile(path, YAML.stringify(yaml));
109
111
  const asset = await this.importFile(path);
110
- console.log('Imported');
111
- this.cache.flushAll();
112
- definitionsManager.clearCache();
112
+ clearAllCaches();
113
113
  const ref = `kapeta://${yaml.metadata.name}:local`;
114
114
  this.maybeGenerateCode(ref, path, yaml);
115
115
  return asset;
@@ -125,11 +125,23 @@ class AssetManager {
125
125
  if (!asset.ymlPath) {
126
126
  throw new Error('Attempted to update corrupted asset: ' + ref);
127
127
  }
128
- console.log('Wrote to ' + asset.ymlPath);
129
- FS.writeFileSync(asset.ymlPath, YAML.stringify(yaml));
130
- this.cache.flushAll();
131
- definitionsManager.clearCache();
132
- this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
128
+ const path = asset.ymlPath;
129
+ try {
130
+ await repositoryManager.ignoreChangesFor(path);
131
+ await FS.writeFile(asset.ymlPath, YAML.stringify(yaml));
132
+ console.log('Wrote to ' + asset.ymlPath);
133
+ clearAllCaches();
134
+ this.maybeGenerateCode(asset.ref, asset.ymlPath, yaml);
135
+ }
136
+ finally {
137
+ //We need to wait a bit for the disk to settle before we can resume watching
138
+ setTimeout(async () => {
139
+ try {
140
+ await repositoryManager.resumeChangedFor(path);
141
+ }
142
+ catch (e) { }
143
+ }, 500);
144
+ }
133
145
  }
134
146
  maybeGenerateCode(ref, ymlPath, block) {
135
147
  ref = normalizeKapetaUri(ref);
@@ -148,14 +160,15 @@ class AssetManager {
148
160
  if (filePath.startsWith('file://')) {
149
161
  filePath = filePath.substring('file://'.length);
150
162
  }
151
- if (!FS.existsSync(filePath)) {
163
+ if (!(await FS.pathExists(filePath))) {
152
164
  throw new Error('File not found: ' + filePath);
153
165
  }
154
- const assetInfos = YAML.parseAllDocuments(FS.readFileSync(filePath).toString()).map((doc) => doc.toJSON());
155
- await Actions.link(progressListener, Path.dirname(filePath));
166
+ const content = await FS.readFile(filePath);
167
+ const assetInfos = YAML.parseAllDocuments(content.toString()).map((doc) => doc.toJSON());
168
+ await Actions.link(new ProgressListener(), Path.dirname(filePath));
156
169
  const version = 'local';
157
170
  const refs = assetInfos.map((assetInfo) => `kapeta://${assetInfo.metadata.name}:${version}`);
158
- this.cache.flushAll();
171
+ clearAllCaches();
159
172
  return this.getAssets().filter((a) => refs.some((ref) => compareRefs(ref, a.ref)));
160
173
  }
161
174
  async unregisterAsset(ref) {
@@ -163,8 +176,8 @@ class AssetManager {
163
176
  if (!asset) {
164
177
  throw new Error('Asset does not exists: ' + ref);
165
178
  }
166
- this.cache.flushAll();
167
- await Actions.uninstall(progressListener, [asset.ref]);
179
+ clearAllCaches();
180
+ await Actions.uninstall(new ProgressListener(), [asset.ref]);
168
181
  }
169
182
  async installAsset(ref) {
170
183
  const asset = await this.getAsset(ref, true, false);
@@ -11,7 +11,6 @@ import md5 from 'md5';
11
11
  import { getBlockInstanceContainerName } from './utils/utils';
12
12
  import { KapetaAPI } from '@kapeta/nodejs-api-client';
13
13
  import { taskManager } from './taskManager';
14
- const EVENT_IMAGE_PULL = 'docker-image-pull';
15
14
  export const CONTAINER_LABEL_PORT_PREFIX = 'kapeta_port-';
16
15
  const NANO_SECOND = 1000000;
17
16
  const HEALTH_CHECK_INTERVAL = 3000;
@@ -2,10 +2,10 @@ import Router from 'express-promise-router';
2
2
  import { KapetaAPI } from '@kapeta/nodejs-api-client';
3
3
  import { corsHandler } from '../middleware/cors';
4
4
  const router = Router();
5
- const api = new KapetaAPI();
6
5
  router.use('/', corsHandler);
7
6
  router.get('/current', async (req, res) => {
8
7
  try {
8
+ const api = new KapetaAPI();
9
9
  if (api.hasToken()) {
10
10
  res.send(await api.getCurrentIdentity());
11
11
  }
@@ -19,6 +19,7 @@ router.get('/current', async (req, res) => {
19
19
  });
20
20
  router.get('/:identityId/memberships', async (req, res) => {
21
21
  try {
22
+ const api = new KapetaAPI();
22
23
  if (api.hasToken()) {
23
24
  res.send(await api.getMemberships(req.params.identityId));
24
25
  }
@@ -38,7 +38,5 @@ export declare class InstanceManager {
38
38
  private checkInstances;
39
39
  private getExternalStatus;
40
40
  private requestInstanceStatus;
41
- private emitSystemEvent;
42
- private emitInstanceEvent;
43
41
  }
44
42
  export declare const instanceManager: InstanceManager;
@@ -3,7 +3,7 @@ import request from 'request';
3
3
  import AsyncLock from 'async-lock';
4
4
  import { BlockInstanceRunner } from './utils/BlockInstanceRunner';
5
5
  import { storageService } from './storageService';
6
- import { socketManager } from './socketManager';
6
+ import { EVENT_INSTANCE_CREATED, EVENT_INSTANCE_EXITED, EVENT_STATUS_CHANGED, socketManager, } from './socketManager';
7
7
  import { serviceManager } from './serviceManager';
8
8
  import { assetManager } from './assetManager';
9
9
  import { containerManager, HEALTH_CHECK_TIMEOUT } from './containerManager';
@@ -16,10 +16,6 @@ import { definitionsManager } from './definitionsManager';
16
16
  import { Task, taskManager } from './taskManager';
17
17
  const CHECK_INTERVAL = 5000;
18
18
  const DEFAULT_HEALTH_PORT_TYPE = 'rest';
19
- const EVENT_STATUS_CHANGED = 'status-changed';
20
- const EVENT_INSTANCE_CREATED = 'instance-created';
21
- const EVENT_INSTANCE_EXITED = 'instance-exited';
22
- const EVENT_INSTANCE_LOG = 'instance-log';
23
19
  const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
24
20
  export class InstanceManager {
25
21
  _interval = undefined;
@@ -55,7 +51,10 @@ export class InstanceManager {
55
51
  return [];
56
52
  }
57
53
  const plan = planInfo.definition;
58
- const instanceIds = plan.spec.blocks.map((block) => block.id);
54
+ if (!plan?.spec?.blocks) {
55
+ return [];
56
+ }
57
+ const instanceIds = plan.spec?.blocks?.map((block) => block.id) || [];
59
58
  return this._instances.filter((instance) => instance.systemId === systemId && instanceIds.includes(instance.instanceId));
60
59
  }
61
60
  getInstance(systemId, instanceId) {
@@ -115,11 +114,11 @@ export class InstanceManager {
115
114
  if (existingInstance) {
116
115
  const ix = this._instances.indexOf(existingInstance);
117
116
  this._instances.splice(ix, 1, instance);
118
- this.emitSystemEvent(instance.systemId, EVENT_STATUS_CHANGED, instance);
117
+ socketManager.emitSystemEvent(instance.systemId, EVENT_STATUS_CHANGED, instance);
119
118
  }
120
119
  else {
121
120
  this._instances.push(instance);
122
- this.emitSystemEvent(instance.systemId, EVENT_INSTANCE_CREATED, instance);
121
+ socketManager.emitSystemEvent(instance.systemId, EVENT_INSTANCE_CREATED, instance);
123
122
  }
124
123
  this.save();
125
124
  return instance;
@@ -162,7 +161,7 @@ export class InstanceManager {
162
161
  if (healthUrl) {
163
162
  instance.health = healthUrl;
164
163
  }
165
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
164
+ socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
166
165
  }
167
166
  else {
168
167
  //If instance was not found - then we're receiving an externally started instance
@@ -178,7 +177,7 @@ export class InstanceManager {
178
177
  address,
179
178
  };
180
179
  this._instances.push(instance);
181
- this.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
180
+ socketManager.emitSystemEvent(systemId, EVENT_INSTANCE_CREATED, instance);
182
181
  }
183
182
  this.save();
184
183
  return instance;
@@ -203,7 +202,7 @@ export class InstanceManager {
203
202
  instance.status = InstanceStatus.STOPPED;
204
203
  instance.pid = null;
205
204
  instance.health = null;
206
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
205
+ socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
207
206
  this.save();
208
207
  }
209
208
  });
@@ -261,7 +260,7 @@ export class InstanceManager {
261
260
  instance.desiredStatus = DesiredInstanceStatus.STOP;
262
261
  }
263
262
  instance.status = InstanceStatus.STOPPING;
264
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
263
+ socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
265
264
  console.log('Stopping instance: %s::%s [desired: %s]', systemId, instanceId, instance.desiredStatus);
266
265
  this.save();
267
266
  try {
@@ -272,7 +271,7 @@ export class InstanceManager {
272
271
  try {
273
272
  await container.stop();
274
273
  instance.status = InstanceStatus.STOPPED;
275
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
274
+ socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
276
275
  this.save();
277
276
  }
278
277
  catch (e) {
@@ -291,7 +290,7 @@ export class InstanceManager {
291
290
  }
292
291
  process.kill(instance.pid, 'SIGTERM');
293
292
  instance.status = InstanceStatus.STOPPED;
294
- this.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
293
+ socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
295
294
  this.save();
296
295
  }
297
296
  catch (e) {
@@ -416,8 +415,8 @@ export class InstanceManager {
416
415
  status: InstanceStatus.FAILED,
417
416
  errorMessage: e.message ?? 'Failed to start - Check logs for details.',
418
417
  });
419
- this.emitInstanceEvent(systemId, instanceId, EVENT_INSTANCE_LOG, logs[0]);
420
- this.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
418
+ socketManager.emitInstanceLog(systemId, instanceId, logs[0]);
419
+ socketManager.emitInstanceEvent(systemId, blockInstance.id, EVENT_INSTANCE_EXITED, {
421
420
  error: `Failed to start instance: ${e.message}`,
422
421
  status: EVENT_INSTANCE_EXITED,
423
422
  instanceId: blockInstance.id,
@@ -506,7 +505,7 @@ export class InstanceManager {
506
505
  const oldStatus = instance.status;
507
506
  instance.status = newStatus;
508
507
  console.log('Instance status changed: %s %s: %s -> %s', instance.systemId, instance.instanceId, oldStatus, instance.status);
509
- this.emitSystemEvent(instance.systemId, EVENT_STATUS_CHANGED, instance);
508
+ socketManager.emitSystemEvent(instance.systemId, EVENT_STATUS_CHANGED, instance);
510
509
  changed = true;
511
510
  }
512
511
  }
@@ -638,24 +637,6 @@ export class InstanceManager {
638
637
  });
639
638
  });
640
639
  }
641
- emitSystemEvent(systemId, type, payload) {
642
- systemId = normalizeKapetaUri(systemId);
643
- try {
644
- socketManager.emit(`${systemId}/instances`, type, payload);
645
- }
646
- catch (e) {
647
- console.warn('Failed to emit instance event: %s', e.message);
648
- }
649
- }
650
- emitInstanceEvent(systemId, instanceId, type, payload) {
651
- systemId = normalizeKapetaUri(systemId);
652
- try {
653
- socketManager.emit(`${systemId}/instances/${instanceId}`, type, payload);
654
- }
655
- catch (e) {
656
- console.warn('Failed to emit instance event: %s', e.message);
657
- }
658
- }
659
640
  }
660
641
  export const instanceManager = new InstanceManager();
661
642
  process.on('exit', async () => {
@@ -1,8 +1,9 @@
1
1
  /// <reference types="node" />
2
- import { SocketManager } from './socketManager';
3
- declare class ProgressListener {
4
- private socketManager;
5
- constructor(socketManager: SocketManager);
2
+ export declare class ProgressListener {
3
+ private readonly systemId;
4
+ private readonly instanceId;
5
+ constructor(systemId?: string, instanceId?: string);
6
+ private emitLog;
6
7
  run(command: string, directory?: string): Promise<{
7
8
  exit: number;
8
9
  signal: NodeJS.Signals | null;
@@ -17,5 +18,3 @@ declare class ProgressListener {
17
18
  info(msg: string, ...args: any[]): void;
18
19
  debug(msg: string, ...args: any[]): void;
19
20
  }
20
- export declare const progressListener: ProgressListener;
21
- export {};