@kapeta/local-cluster-service 0.36.1 → 0.38.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 (54) hide show
  1. package/.vscode/launch.json +9 -5
  2. package/CHANGELOG.md +21 -0
  3. package/dist/cjs/src/assetManager.d.ts +2 -1
  4. package/dist/cjs/src/assetManager.js +7 -4
  5. package/dist/cjs/src/containerManager.d.ts +6 -3
  6. package/dist/cjs/src/containerManager.js +96 -18
  7. package/dist/cjs/src/instanceManager.d.ts +16 -0
  8. package/dist/cjs/src/instanceManager.js +78 -14
  9. package/dist/cjs/src/operatorManager.d.ts +3 -4
  10. package/dist/cjs/src/operatorManager.js +3 -4
  11. package/dist/cjs/src/progressListener.d.ts +8 -1
  12. package/dist/cjs/src/progressListener.js +12 -1
  13. package/dist/cjs/src/repositoryManager.js +3 -2
  14. package/dist/cjs/src/taskManager.d.ts +2 -0
  15. package/dist/cjs/src/taskManager.js +9 -0
  16. package/dist/cjs/src/types.d.ts +4 -19
  17. package/dist/cjs/src/types.js +5 -1
  18. package/dist/cjs/src/utils/BlockInstanceRunner.js +16 -5
  19. package/dist/cjs/src/utils/commandLineUtils.d.ts +2 -1
  20. package/dist/cjs/src/utils/commandLineUtils.js +7 -1
  21. package/dist/cjs/src/utils/utils.d.ts +3 -3
  22. package/dist/cjs/src/utils/utils.js +4 -3
  23. package/dist/esm/src/assetManager.d.ts +2 -1
  24. package/dist/esm/src/assetManager.js +7 -4
  25. package/dist/esm/src/containerManager.d.ts +6 -3
  26. package/dist/esm/src/containerManager.js +96 -18
  27. package/dist/esm/src/instanceManager.d.ts +16 -0
  28. package/dist/esm/src/instanceManager.js +78 -14
  29. package/dist/esm/src/operatorManager.d.ts +3 -4
  30. package/dist/esm/src/operatorManager.js +3 -4
  31. package/dist/esm/src/progressListener.d.ts +8 -1
  32. package/dist/esm/src/progressListener.js +12 -1
  33. package/dist/esm/src/repositoryManager.js +3 -2
  34. package/dist/esm/src/taskManager.d.ts +2 -0
  35. package/dist/esm/src/taskManager.js +9 -0
  36. package/dist/esm/src/types.d.ts +4 -19
  37. package/dist/esm/src/types.js +5 -1
  38. package/dist/esm/src/utils/BlockInstanceRunner.js +16 -5
  39. package/dist/esm/src/utils/commandLineUtils.d.ts +2 -1
  40. package/dist/esm/src/utils/commandLineUtils.js +7 -1
  41. package/dist/esm/src/utils/utils.d.ts +3 -3
  42. package/dist/esm/src/utils/utils.js +4 -3
  43. package/package.json +17 -16
  44. package/src/assetManager.ts +9 -7
  45. package/src/containerManager.ts +110 -23
  46. package/src/instanceManager.ts +87 -16
  47. package/src/operatorManager.ts +11 -6
  48. package/src/progressListener.ts +15 -1
  49. package/src/repositoryManager.ts +5 -3
  50. package/src/taskManager.ts +11 -0
  51. package/src/types.ts +5 -19
  52. package/src/utils/BlockInstanceRunner.ts +21 -8
  53. package/src/utils/commandLineUtils.ts +10 -2
  54. package/src/utils/utils.ts +5 -6
@@ -25,14 +25,16 @@ import {
25
25
  InstanceOwner,
26
26
  InstanceStatus,
27
27
  InstanceType,
28
- LocalImageOptions,
28
+ KIND_BLOCK_TYPE_EXECUTABLE,
29
+ KIND_BLOCK_TYPE_OPERATOR,
30
+ KIND_RESOURCE_OPERATOR,
29
31
  LogEntry,
30
32
  OperatorInstanceInfo,
31
33
  OperatorInstancePort,
32
34
  } from './types';
33
- import { BlockDefinitionSpec, BlockInstance, Plan } from '@kapeta/schemas';
35
+ import { BlockDefinitionSpec, LocalInstance, Plan } from '@kapeta/schemas';
34
36
  import { getBlockInstanceContainerName, getResolvedConfiguration } from './utils/utils';
35
- import { KIND_BLOCK_OPERATOR, KIND_RESOURCE_OPERATOR, operatorManager } from './operatorManager';
37
+ import { operatorManager } from './operatorManager';
36
38
  import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
37
39
  import { definitionsManager } from './definitionsManager';
38
40
  import { Task, taskManager } from './taskManager';
@@ -290,7 +292,9 @@ export class InstanceManager {
290
292
  systemId = normalizeKapetaUri(systemId);
291
293
  const instance = _.find(this._instances, { systemId, instanceId });
292
294
  if (instance && instance.owner === InstanceOwner.EXTERNAL && instance.status !== InstanceStatus.STOPPED) {
293
- instance.status = InstanceStatus.STOPPED;
295
+ if (instance.status != InstanceStatus.FAILED) {
296
+ instance.status = InstanceStatus.STOPPED;
297
+ }
294
298
  instance.pid = null;
295
299
  instance.health = null;
296
300
  socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
@@ -313,12 +317,13 @@ export class InstanceManager {
313
317
  return taskManager.add(
314
318
  `plan:start:${systemId}`,
315
319
  async () => {
316
- let promises: Promise<InstanceInfo>[] = [];
317
- let errors = [];
318
- for (let blockInstance of Object.values(plan.spec.blocks as BlockInstance[])) {
320
+ const promises: Promise<InstanceInfo>[] = [];
321
+ const errors = [];
322
+ const instanceIds = await this.getAllInstancesExceptKind(systemId, KIND_BLOCK_TYPE_EXECUTABLE);
323
+ for (const instanceId of instanceIds) {
319
324
  try {
320
325
  promises.push(
321
- this.start(systemId, blockInstance.id).then((taskOrInstance) => {
326
+ this.start(systemId, instanceId).then((taskOrInstance) => {
322
327
  if (taskOrInstance instanceof Task) {
323
328
  return taskOrInstance.wait();
324
329
  }
@@ -381,7 +386,7 @@ export class InstanceManager {
381
386
  throw new Error(`Operator block has no local definition: ${blockRef}`);
382
387
  }
383
388
 
384
- const localConfig = operatorDefinition.definition.spec.local as LocalImageOptions;
389
+ const localConfig = operatorDefinition.definition.spec.local as LocalInstance;
385
390
 
386
391
  let instance = await this.start(systemId, instanceId);
387
392
  if (instance instanceof Task) {
@@ -662,8 +667,7 @@ export class InstanceManager {
662
667
 
663
668
  const out = await this.saveInternalInstance({
664
669
  ...instance,
665
- type: InstanceType.UNKNOWN,
666
- pid: null,
670
+ type: InstanceType.DOCKER,
667
671
  health: null,
668
672
  portType: DEFAULT_HEALTH_PORT_TYPE,
669
673
  status: InstanceStatus.FAILED,
@@ -807,7 +811,6 @@ export class InstanceManager {
807
811
  if (instance.status !== newStatus) {
808
812
  const oldStatus = instance.status;
809
813
  const skipUpdate =
810
- (newStatus === InstanceStatus.STOPPED && instance.status === InstanceStatus.FAILED) ||
811
814
  ([InstanceStatus.READY, InstanceStatus.UNHEALTHY].includes(newStatus) &&
812
815
  instance.status === InstanceStatus.STOPPING) ||
813
816
  (newStatus === InstanceStatus.STOPPED &&
@@ -831,7 +834,7 @@ export class InstanceManager {
831
834
 
832
835
  if (
833
836
  instance.desiredStatus === DesiredInstanceStatus.RUN &&
834
- [InstanceStatus.STOPPED, InstanceStatus.FAILED, InstanceStatus.STOPPING].includes(newStatus)
837
+ [InstanceStatus.STOPPED, InstanceStatus.STOPPING].includes(newStatus)
835
838
  ) {
836
839
  //If the instance is stopped but we want it to run, start it
837
840
  try {
@@ -924,11 +927,18 @@ export class InstanceManager {
924
927
  }
925
928
 
926
929
  if (statusType === 'created') {
930
+ if (state.ExitCode !== 0) {
931
+ // Failed during creation
932
+ return InstanceStatus.FAILED;
933
+ }
927
934
  return InstanceStatus.STARTING;
928
935
  }
929
936
 
930
937
  if (statusType === 'exited' || statusType === 'dead') {
931
- return InstanceStatus.STOPPED;
938
+ if (state.ExitCode === 0) {
939
+ return InstanceStatus.STOPPED;
940
+ }
941
+ return InstanceStatus.FAILED;
932
942
  }
933
943
 
934
944
  if (statusType === 'removing') {
@@ -1008,8 +1018,8 @@ export class InstanceManager {
1008
1018
  return false;
1009
1019
  }
1010
1020
 
1011
- if (parseKapetaUri(provider.kind).fullName === KIND_BLOCK_OPERATOR) {
1012
- const localConfig = provider.data.spec.local as LocalImageOptions;
1021
+ if (parseKapetaUri(provider.kind).fullName === KIND_BLOCK_TYPE_OPERATOR) {
1022
+ const localConfig = provider.data.spec.local as LocalInstance;
1013
1023
  return localConfig.singleton ?? false;
1014
1024
  }
1015
1025
 
@@ -1025,6 +1035,37 @@ export class InstanceManager {
1025
1035
  return block.data.kind;
1026
1036
  }
1027
1037
 
1038
+ /**
1039
+ * Get the kind of an asset. Use the maxDepth parameter to specify how deep to look for the
1040
+ * kind. For example, if maxDepth is 2, the method will look for the kind of the asset and then
1041
+ * the kind of the kind.
1042
+ * @param assetRef The asset reference
1043
+ * @param maxDepth The maximum depth to look for the kind
1044
+ * @returns The kind of the asset or null if not found
1045
+ */
1046
+ private async getDeepKindForAssetRef(assetRef: string, maxDepth: number): Promise<string | null> {
1047
+ if (maxDepth <= 0) {
1048
+ return null;
1049
+ }
1050
+
1051
+ try {
1052
+ const asset = await assetManager.getAsset(assetRef);
1053
+ if (!asset || !asset.data.kind) {
1054
+ return null;
1055
+ }
1056
+
1057
+ if (maxDepth === 1) {
1058
+ return asset.data.kind;
1059
+ } else {
1060
+ // Recurse with the kind of the current block and one less depth
1061
+ return await this.getDeepKindForAssetRef(asset.data.kind, maxDepth - 1);
1062
+ }
1063
+ } catch (error) {
1064
+ console.error('Error fetching kind for assetRef:', assetRef, error);
1065
+ return null;
1066
+ }
1067
+ }
1068
+
1028
1069
  private async isUsingKind(ref: string, kind: string): Promise<boolean> {
1029
1070
  const assetKind = await this.getKindForAssetRef(ref);
1030
1071
  if (!assetKind) {
@@ -1048,6 +1089,36 @@ export class InstanceManager {
1048
1089
 
1049
1090
  return out;
1050
1091
  }
1092
+
1093
+ /**
1094
+ * Get the ids for all block instances except the ones of the specified kind
1095
+ * @param systemId The plan reference id
1096
+ * @param kind The kind to exclude. Can be a string or an array of strings
1097
+ * @returns An array of block instance ids
1098
+ */
1099
+ private async getAllInstancesExceptKind(systemId: string, kind: string | string[]): Promise<string[]> {
1100
+ const plan = await assetManager.getPlan(systemId);
1101
+ if (!plan?.spec?.blocks) {
1102
+ return [];
1103
+ }
1104
+ const out: string[] = [];
1105
+ const excludedKinds = kind instanceof Array ? kind : [kind];
1106
+ for (const block of plan.spec.blocks) {
1107
+ const blockKindOfKind = await this.getDeepKindForAssetRef(block.block.ref, 2);
1108
+ if (!blockKindOfKind) {
1109
+ continue;
1110
+ }
1111
+
1112
+ const shouldIncludeBlock =
1113
+ excludedKinds.some((excludedKind) => excludedKind === parseKapetaUri(blockKindOfKind).fullName) ===
1114
+ false;
1115
+ if (shouldIncludeBlock) {
1116
+ out.push(block.id);
1117
+ }
1118
+ }
1119
+
1120
+ return out;
1121
+ }
1051
1122
  }
1052
1123
 
1053
1124
  export const instanceManager = new InstanceManager();
@@ -16,8 +16,15 @@ import {
16
16
  containerManager,
17
17
  } from './containerManager';
18
18
  import FSExtra from 'fs-extra';
19
- import { AnyMap, EnvironmentType, LocalImageOptions, OperatorInfo, StringMap } from './types';
20
- import { BlockInstance, Resource } from '@kapeta/schemas';
19
+ import {
20
+ AnyMap,
21
+ EnvironmentType,
22
+ KIND_BLOCK_TYPE_OPERATOR,
23
+ KIND_RESOURCE_OPERATOR,
24
+ OperatorInfo,
25
+ StringMap,
26
+ } from './types';
27
+ import { BlockInstance, LocalInstance, Resource } from '@kapeta/schemas';
21
28
  import { definitionsManager } from './definitionsManager';
22
29
  import { getBindHost, toPortInfo } from './utils/utils';
23
30
  import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
@@ -25,8 +32,6 @@ import _ from 'lodash';
25
32
  import AsyncLock from 'async-lock';
26
33
  import { taskManager } from './taskManager';
27
34
 
28
- export const KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
29
- export const KIND_BLOCK_OPERATOR = 'core/block-type-operator';
30
35
  const KIND_PLAN = 'core/plan';
31
36
 
32
37
  class Operator {
@@ -36,7 +41,7 @@ class Operator {
36
41
  this._data = data;
37
42
  }
38
43
 
39
- getLocalData(): LocalImageOptions {
44
+ getLocalData(): LocalInstance {
40
45
  return this._data.definition.spec.local;
41
46
  }
42
47
 
@@ -68,7 +73,7 @@ class OperatorManager {
68
73
  * Get operator definition for resource type
69
74
  */
70
75
  async getOperator(fullName: string, version: string) {
71
- const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR, KIND_BLOCK_OPERATOR]);
76
+ const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR, KIND_BLOCK_TYPE_OPERATOR]);
72
77
 
73
78
  const operator: DefinitionInfo | undefined = operators.find(
74
79
  (operator) =>
@@ -7,6 +7,7 @@ import { spawn } from '@kapeta/nodejs-process';
7
7
  import { socketManager } from './socketManager';
8
8
  import { LogEntry } from './types';
9
9
  import { format } from 'node:util';
10
+ import { Task } from './taskManager';
10
11
 
11
12
  export class ProgressListener {
12
13
  private readonly systemId: string | undefined;
@@ -17,7 +18,7 @@ export class ProgressListener {
17
18
  this.instanceId = instanceId;
18
19
  }
19
20
 
20
- private emitLog(payload: Omit<LogEntry, 'time' | 'source'>) {
21
+ protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>) {
21
22
  const logEntry: LogEntry = {
22
23
  ...payload,
23
24
  source: 'stdout',
@@ -135,3 +136,16 @@ export class ProgressListener {
135
136
  });
136
137
  }
137
138
  }
139
+
140
+ export class TaskProgressListener extends ProgressListener {
141
+ private readonly task: Task;
142
+
143
+ constructor(task: Task) {
144
+ super();
145
+ this.task = task;
146
+ }
147
+
148
+ protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>) {
149
+ this.task.addLog(payload.message, payload.level);
150
+ }
151
+ }
@@ -10,7 +10,7 @@ import { Actions, AssetVersion, Config, RegistryService } from '@kapeta/nodejs-r
10
10
  import { definitionsManager } from './definitionsManager';
11
11
  import { Task, taskManager } from './taskManager';
12
12
  import { normalizeKapetaUri, parseKapetaUri, parseVersion } from '@kapeta/nodejs-utils';
13
- import { ProgressListener } from './progressListener';
13
+ import { TaskProgressListener } from './progressListener';
14
14
  import { RepositoryWatcher } from './RepositoryWatcher';
15
15
  import { SourceOfChange } from './types';
16
16
  import { cacheManager } from './cacheManager';
@@ -191,16 +191,18 @@ class RepositoryManager extends EventEmitter {
191
191
  private async scheduleInstallation(refs: string[]): Promise<Task[]> {
192
192
  //We make sure to only install one asset at a time - otherwise unexpected things might happen
193
193
  const createInstaller = (ref: string) => {
194
- return async () => {
194
+ return async (task: Task) => {
195
195
  if (await definitionsManager.exists(ref)) {
196
196
  return;
197
197
  }
198
+
199
+ const progressListener = new TaskProgressListener(task);
198
200
  //console.log(`Installing asset: ${ref}`);
199
201
  //Auto-install missing asset
200
202
  try {
201
203
  //We change to a temp dir to avoid issues with the current working directory
202
204
  process.chdir(os.tmpdir());
203
- await Actions.install(new ProgressListener(), [ref], {});
205
+ await Actions.install(progressListener, [ref], {});
204
206
  } catch (e) {
205
207
  console.error(`Failed to install asset: ${ref}`, e);
206
208
  throw e;
@@ -7,10 +7,12 @@
7
7
  * Class that handles processing background tasks.
8
8
  */
9
9
  import { socketManager } from './socketManager';
10
+ import { LogLevel } from './types';
10
11
 
11
12
  const EVENT_TASK_UPDATED = 'task-updated';
12
13
  const EVENT_TASK_ADDED = 'task-added';
13
14
  const EVENT_TASK_REMOVED = 'task-removed';
15
+ const EVENT_TASK_LOG = 'task-log';
14
16
 
15
17
  export type TaskRunner<T> = (task: Task<T>) => Promise<T>;
16
18
 
@@ -94,6 +96,15 @@ export class Task<T = void> implements TaskData<T> {
94
96
  socketManager.emitGlobal(EVENT_TASK_UPDATED, this.toData());
95
97
  }
96
98
 
99
+ public addLog(log: string, level: LogLevel = 'INFO') {
100
+ socketManager.emitGlobal(EVENT_TASK_LOG, {
101
+ id: this.id,
102
+ message: log,
103
+ level,
104
+ time: Date.now(),
105
+ });
106
+ }
107
+
97
108
  async wait(): Promise<T> {
98
109
  return this.future.promise;
99
110
  }
package/src/types.ts CHANGED
@@ -8,6 +8,11 @@ import { Connection, Resource } from '@kapeta/schemas';
8
8
  import { StringBodyRequest } from './middleware/stringBody';
9
9
  import { KapetaRequest } from './middleware/kapeta';
10
10
 
11
+ export const KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
12
+ export const KIND_BLOCK_TYPE = 'core/block-type';
13
+ export const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
14
+ export const KIND_BLOCK_TYPE_EXECUTABLE = 'core/block-type-executable';
15
+
11
16
  export type StringMap = { [key: string]: string };
12
17
  export type AnyMap = { [key: string]: any };
13
18
  export type SourceOfChange = 'user' | 'filesystem';
@@ -68,20 +73,6 @@ export interface Health {
68
73
  retries?: number;
69
74
  }
70
75
 
71
- export type PortInfo = { port: number; type: 'tcp' | 'udp' } | number | string;
72
-
73
- export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
74
- image: string;
75
- ports: { [key: string]: PortInfo };
76
- credentials?: Credentials;
77
- options?: Options;
78
- cmd?: string;
79
- env?: AnyMap;
80
- health?: Health;
81
- singleton?: boolean;
82
- mounts?: { [key: string]: string };
83
- };
84
-
85
76
  export type InstanceInfo = {
86
77
  systemId: string;
87
78
  instanceId: string;
@@ -100,11 +91,6 @@ export type InstanceInfo = {
100
91
  portType?: string;
101
92
  };
102
93
 
103
- interface ResourceRef {
104
- blockId: string;
105
- resourceName: string;
106
- }
107
-
108
94
  export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response, info: ProxyRequestInfo) => void;
109
95
 
110
96
  export interface OperatorInstancePort {
@@ -18,13 +18,14 @@ import {
18
18
  } from '../containerManager';
19
19
  import { LogData } from './LogData';
20
20
  import { clusterService } from '../clusterService';
21
- import { AnyMap, BlockProcessParams, InstanceType, LocalImageOptions, ProcessInfo, StringMap } from '../types';
21
+ import { AnyMap, BlockProcessParams, InstanceType, KIND_BLOCK_TYPE_OPERATOR, ProcessInfo, StringMap } from '../types';
22
22
  import { definitionsManager } from '../definitionsManager';
23
23
  import Docker from 'dockerode';
24
24
  import OS from 'node:os';
25
+ import Path from 'node:path';
25
26
  import { taskManager } from '../taskManager';
27
+ import { LocalDevContainer, LocalInstance } from '@kapeta/schemas';
26
28
 
27
- const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
28
29
  const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
29
30
  const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
30
31
  const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
@@ -182,6 +183,8 @@ export class BlockInstanceRunner {
182
183
  throw new Error('Missing target kind in block definition');
183
184
  }
184
185
 
186
+ const realLocalPath = await FSExtra.realpath(baseDir);
187
+
185
188
  const kindUri = parseKapetaUri(assetVersion.definition.kind);
186
189
 
187
190
  const providerVersion = await getProvider(kindUri);
@@ -198,17 +201,29 @@ export class BlockInstanceRunner {
198
201
  throw new Error(`Target not found: ${targetKindUri.id}`);
199
202
  }
200
203
 
201
- const localContainer = targetVersion.definition.spec.local;
204
+ const localContainer = targetVersion.definition.spec.local as LocalDevContainer;
202
205
 
203
206
  if (!localContainer) {
204
207
  throw new Error(`Missing local container information from target: ${targetKindUri.id}`);
205
208
  }
206
209
 
207
- const dockerImage = localContainer.image;
208
- if (!dockerImage) {
210
+ let dockerImage = localContainer.image;
211
+ const isDockerImage = !localContainer.type || localContainer.type.toLowerCase() === 'docker';
212
+ const isDockerFile = Boolean(localContainer.type && localContainer.type.toLowerCase() === 'dockerfile');
213
+ if (isDockerImage && !dockerImage) {
209
214
  throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
210
215
  }
211
216
 
217
+ if (isDockerFile) {
218
+ dockerImage = blockInfo.fullName + ':local';
219
+ const dockerFile = Path.join(realLocalPath, localContainer.file ?? 'Dockerfile');
220
+ if (!FSExtra.existsSync(dockerFile)) {
221
+ throw new Error(`Dockerfile not found at: ${dockerFile}`);
222
+ }
223
+ const task = containerManager.buildDockerImage(dockerFile, blockInfo.fullName + ':local');
224
+ await task.wait();
225
+ }
226
+
212
227
  const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, targetKindUri.id);
213
228
  const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
214
229
  const dockerOpts = localContainer.options ?? {};
@@ -235,8 +250,6 @@ export class BlockInstanceRunner {
235
250
  HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
236
251
  }
237
252
 
238
- const realLocalPath = await FSExtra.realpath(baseDir);
239
-
240
253
  const Mounts = containerManager.toDockerMounts({
241
254
  [workingDir]: toLocalBindVolume(realLocalPath),
242
255
  });
@@ -384,7 +397,7 @@ export class BlockInstanceRunner {
384
397
  throw new Error(`Provider did not have local image: ${providerRef}`);
385
398
  }
386
399
 
387
- const local = spec.local as LocalImageOptions;
400
+ const local = spec.local as LocalInstance;
388
401
 
389
402
  const dockerImage = local.image;
390
403
  const operatorUri = local.singleton ? parseKapetaUri(providerRef) : blockUri;
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { spawn, hasApp } from '@kapeta/nodejs-process';
7
- import { taskManager } from '../taskManager';
7
+ import { Task, taskManager } from '../taskManager';
8
8
 
9
9
  export async function hasCLI() {
10
10
  return hasApp('kap');
@@ -17,11 +17,19 @@ export async function ensureCLI() {
17
17
 
18
18
  return taskManager.add(
19
19
  `cli:install`,
20
- () => {
20
+ (task: Task) => {
21
21
  const process = spawn('npm', ['install', '-g', '@kapeta/kap'], {
22
22
  shell: true,
23
23
  });
24
24
 
25
+ process.process.stdout?.on('data', (data: any) => {
26
+ task.addLog(data.toString(), 'INFO');
27
+ });
28
+
29
+ process.process.stderr?.on('data', (data: any) => {
30
+ task.addLog(data.toString(), 'ERROR');
31
+ });
32
+
25
33
  return process.wait();
26
34
  },
27
35
  {
@@ -6,13 +6,12 @@
6
6
  import FS from 'node:fs';
7
7
  import YAML from 'yaml';
8
8
  import md5 from 'md5';
9
- import { EntityList } from '@kapeta/schemas';
9
+ import { EntityList, LocalInstancePort, LocalInstancePortType } from '@kapeta/schemas';
10
10
  import _ from 'lodash';
11
- import { AnyMap, PortInfo } from '../types';
11
+ import { AnyMap, KIND_BLOCK_TYPE_OPERATOR } from '../types';
12
12
  import ClusterConfiguration from '@kapeta/local-cluster-config';
13
13
  import { definitionsManager } from '../definitionsManager';
14
14
  import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
15
- import { KIND_BLOCK_OPERATOR } from '../operatorManager';
16
15
  import { assetManager } from '../assetManager';
17
16
 
18
17
  export async function getBlockInstanceContainerName(systemId: string, instanceId: string, blockType?: string) {
@@ -32,7 +31,7 @@ export async function getBlockInstanceContainerName(systemId: string, instanceId
32
31
  throw new Error(`Block type ${blockType} not found`);
33
32
  }
34
33
  if (
35
- parseKapetaUri(typeDefinition.definition.kind).fullName === KIND_BLOCK_OPERATOR &&
34
+ parseKapetaUri(typeDefinition.definition.kind).fullName === KIND_BLOCK_TYPE_OPERATOR &&
36
35
  typeDefinition.definition.spec?.local?.singleton
37
36
  ) {
38
37
  return `kapeta-instance-operator-${md5(normalizeKapetaUri(systemId) + normalizeKapetaUri(blockType))}`;
@@ -41,13 +40,13 @@ export async function getBlockInstanceContainerName(systemId: string, instanceId
41
40
  return `kapeta-block-instance-${md5(normalizeKapetaUri(systemId) + instanceId)}`;
42
41
  }
43
42
 
44
- export function toPortInfo(port: PortInfo) {
43
+ export function toPortInfo(port: LocalInstancePort) {
45
44
  if (typeof port === 'number' || typeof port === 'string') {
46
45
  return { port: parseInt(`${port}`), type: 'tcp' };
47
46
  }
48
47
 
49
48
  if (!port.type) {
50
- port.type = 'tcp';
49
+ port.type = LocalInstancePortType.TCP;
51
50
  }
52
51
 
53
52
  return port;