@kapeta/local-cluster-service 0.0.0-96f91ef

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 (274) hide show
  1. package/.eslintrc.cjs +25 -0
  2. package/.github/workflows/check-license.yml +17 -0
  3. package/.github/workflows/main.yml +26 -0
  4. package/.prettierignore +4 -0
  5. package/.vscode/launch.json +19 -0
  6. package/CHANGELOG.md +920 -0
  7. package/LICENSE +38 -0
  8. package/README.md +36 -0
  9. package/definitions.d.ts +35 -0
  10. package/dist/cjs/index.d.ts +34 -0
  11. package/dist/cjs/index.js +263 -0
  12. package/dist/cjs/package.json +1 -0
  13. package/dist/cjs/src/RepositoryWatcher.d.ts +30 -0
  14. package/dist/cjs/src/RepositoryWatcher.js +332 -0
  15. package/dist/cjs/src/ai/aiClient.d.ts +20 -0
  16. package/dist/cjs/src/ai/aiClient.js +74 -0
  17. package/dist/cjs/src/ai/routes.d.ts +7 -0
  18. package/dist/cjs/src/ai/routes.js +37 -0
  19. package/dist/cjs/src/ai/transform.d.ts +11 -0
  20. package/dist/cjs/src/ai/transform.js +239 -0
  21. package/dist/cjs/src/ai/types.d.ts +40 -0
  22. package/dist/cjs/src/ai/types.js +2 -0
  23. package/dist/cjs/src/api.d.ts +7 -0
  24. package/dist/cjs/src/api.js +29 -0
  25. package/dist/cjs/src/assetManager.d.ts +41 -0
  26. package/dist/cjs/src/assetManager.js +274 -0
  27. package/dist/cjs/src/assets/routes.d.ts +7 -0
  28. package/dist/cjs/src/assets/routes.js +165 -0
  29. package/dist/cjs/src/attachments/routes.d.ts +7 -0
  30. package/dist/cjs/src/attachments/routes.js +72 -0
  31. package/dist/cjs/src/authManager.d.ts +16 -0
  32. package/dist/cjs/src/authManager.js +64 -0
  33. package/dist/cjs/src/cacheManager.d.ts +20 -0
  34. package/dist/cjs/src/cacheManager.js +51 -0
  35. package/dist/cjs/src/clusterService.d.ts +44 -0
  36. package/dist/cjs/src/clusterService.js +120 -0
  37. package/dist/cjs/src/codeGeneratorManager.d.ts +14 -0
  38. package/dist/cjs/src/codeGeneratorManager.js +93 -0
  39. package/dist/cjs/src/config/routes.d.ts +7 -0
  40. package/dist/cjs/src/config/routes.js +160 -0
  41. package/dist/cjs/src/configManager.d.ts +42 -0
  42. package/dist/cjs/src/configManager.js +136 -0
  43. package/dist/cjs/src/containerManager.d.ts +148 -0
  44. package/dist/cjs/src/containerManager.js +958 -0
  45. package/dist/cjs/src/definitionsManager.d.ts +20 -0
  46. package/dist/cjs/src/definitionsManager.js +171 -0
  47. package/dist/cjs/src/filesystem/routes.d.ts +7 -0
  48. package/dist/cjs/src/filesystem/routes.js +105 -0
  49. package/dist/cjs/src/filesystemManager.d.ts +27 -0
  50. package/dist/cjs/src/filesystemManager.js +118 -0
  51. package/dist/cjs/src/identities/routes.d.ts +7 -0
  52. package/dist/cjs/src/identities/routes.js +37 -0
  53. package/dist/cjs/src/instanceManager.d.ts +69 -0
  54. package/dist/cjs/src/instanceManager.js +910 -0
  55. package/dist/cjs/src/instances/routes.d.ts +7 -0
  56. package/dist/cjs/src/instances/routes.js +179 -0
  57. package/dist/cjs/src/middleware/cors.d.ts +6 -0
  58. package/dist/cjs/src/middleware/cors.js +14 -0
  59. package/dist/cjs/src/middleware/kapeta.d.ts +15 -0
  60. package/dist/cjs/src/middleware/kapeta.js +28 -0
  61. package/dist/cjs/src/middleware/stringBody.d.ts +9 -0
  62. package/dist/cjs/src/middleware/stringBody.js +18 -0
  63. package/dist/cjs/src/networkManager.d.ts +37 -0
  64. package/dist/cjs/src/networkManager.js +119 -0
  65. package/dist/cjs/src/operatorManager.d.ts +41 -0
  66. package/dist/cjs/src/operatorManager.js +211 -0
  67. package/dist/cjs/src/progressListener.d.ts +31 -0
  68. package/dist/cjs/src/progressListener.js +133 -0
  69. package/dist/cjs/src/providerManager.d.ts +11 -0
  70. package/dist/cjs/src/providerManager.js +84 -0
  71. package/dist/cjs/src/providers/routes.d.ts +7 -0
  72. package/dist/cjs/src/providers/routes.js +46 -0
  73. package/dist/cjs/src/proxy/routes.d.ts +7 -0
  74. package/dist/cjs/src/proxy/routes.js +115 -0
  75. package/dist/cjs/src/proxy/types/rest.d.ts +10 -0
  76. package/dist/cjs/src/proxy/types/rest.js +123 -0
  77. package/dist/cjs/src/proxy/types/web.d.ts +8 -0
  78. package/dist/cjs/src/proxy/types/web.js +61 -0
  79. package/dist/cjs/src/repositoryManager.d.ts +35 -0
  80. package/dist/cjs/src/repositoryManager.js +247 -0
  81. package/dist/cjs/src/serviceManager.d.ts +36 -0
  82. package/dist/cjs/src/serviceManager.js +106 -0
  83. package/dist/cjs/src/socketManager.d.ts +32 -0
  84. package/dist/cjs/src/socketManager.js +125 -0
  85. package/dist/cjs/src/storageService.d.ts +21 -0
  86. package/dist/cjs/src/storageService.js +81 -0
  87. package/dist/cjs/src/taskManager.d.ts +70 -0
  88. package/dist/cjs/src/taskManager.js +181 -0
  89. package/dist/cjs/src/tasks/routes.d.ts +7 -0
  90. package/dist/cjs/src/tasks/routes.js +39 -0
  91. package/dist/cjs/src/traffic/routes.d.ts +7 -0
  92. package/dist/cjs/src/traffic/routes.js +22 -0
  93. package/dist/cjs/src/types.d.ts +99 -0
  94. package/dist/cjs/src/types.js +39 -0
  95. package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +28 -0
  96. package/dist/cjs/src/utils/BlockInstanceRunner.js +432 -0
  97. package/dist/cjs/src/utils/DefaultProviderInstaller.d.ts +15 -0
  98. package/dist/cjs/src/utils/DefaultProviderInstaller.js +136 -0
  99. package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
  100. package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
  101. package/dist/cjs/src/utils/LogData.d.ts +23 -0
  102. package/dist/cjs/src/utils/LogData.js +46 -0
  103. package/dist/cjs/src/utils/commandLineUtils.d.ts +8 -0
  104. package/dist/cjs/src/utils/commandLineUtils.js +39 -0
  105. package/dist/cjs/src/utils/pathTemplateParser.d.ts +30 -0
  106. package/dist/cjs/src/utils/pathTemplateParser.js +135 -0
  107. package/dist/cjs/src/utils/utils.d.ts +40 -0
  108. package/dist/cjs/src/utils/utils.js +148 -0
  109. package/dist/cjs/start.d.ts +5 -0
  110. package/dist/cjs/start.js +17 -0
  111. package/dist/cjs/test/proxy/types/rest.test.d.ts +5 -0
  112. package/dist/cjs/test/proxy/types/rest.test.js +48 -0
  113. package/dist/cjs/test/utils/pathTemplateParser.test.d.ts +5 -0
  114. package/dist/cjs/test/utils/pathTemplateParser.test.js +27 -0
  115. package/dist/esm/index.d.ts +34 -0
  116. package/dist/esm/index.js +263 -0
  117. package/dist/esm/package.json +1 -0
  118. package/dist/esm/src/RepositoryWatcher.d.ts +30 -0
  119. package/dist/esm/src/RepositoryWatcher.js +332 -0
  120. package/dist/esm/src/ai/aiClient.d.ts +20 -0
  121. package/dist/esm/src/ai/aiClient.js +74 -0
  122. package/dist/esm/src/ai/routes.d.ts +7 -0
  123. package/dist/esm/src/ai/routes.js +37 -0
  124. package/dist/esm/src/ai/transform.d.ts +11 -0
  125. package/dist/esm/src/ai/transform.js +239 -0
  126. package/dist/esm/src/ai/types.d.ts +40 -0
  127. package/dist/esm/src/ai/types.js +2 -0
  128. package/dist/esm/src/api.d.ts +7 -0
  129. package/dist/esm/src/api.js +29 -0
  130. package/dist/esm/src/assetManager.d.ts +41 -0
  131. package/dist/esm/src/assetManager.js +274 -0
  132. package/dist/esm/src/assets/routes.d.ts +7 -0
  133. package/dist/esm/src/assets/routes.js +165 -0
  134. package/dist/esm/src/attachments/routes.d.ts +7 -0
  135. package/dist/esm/src/attachments/routes.js +72 -0
  136. package/dist/esm/src/authManager.d.ts +16 -0
  137. package/dist/esm/src/authManager.js +64 -0
  138. package/dist/esm/src/cacheManager.d.ts +20 -0
  139. package/dist/esm/src/cacheManager.js +51 -0
  140. package/dist/esm/src/clusterService.d.ts +44 -0
  141. package/dist/esm/src/clusterService.js +120 -0
  142. package/dist/esm/src/codeGeneratorManager.d.ts +14 -0
  143. package/dist/esm/src/codeGeneratorManager.js +93 -0
  144. package/dist/esm/src/config/routes.d.ts +7 -0
  145. package/dist/esm/src/config/routes.js +160 -0
  146. package/dist/esm/src/configManager.d.ts +42 -0
  147. package/dist/esm/src/configManager.js +136 -0
  148. package/dist/esm/src/containerManager.d.ts +148 -0
  149. package/dist/esm/src/containerManager.js +958 -0
  150. package/dist/esm/src/definitionsManager.d.ts +20 -0
  151. package/dist/esm/src/definitionsManager.js +171 -0
  152. package/dist/esm/src/filesystem/routes.d.ts +7 -0
  153. package/dist/esm/src/filesystem/routes.js +105 -0
  154. package/dist/esm/src/filesystemManager.d.ts +27 -0
  155. package/dist/esm/src/filesystemManager.js +118 -0
  156. package/dist/esm/src/identities/routes.d.ts +7 -0
  157. package/dist/esm/src/identities/routes.js +37 -0
  158. package/dist/esm/src/instanceManager.d.ts +69 -0
  159. package/dist/esm/src/instanceManager.js +910 -0
  160. package/dist/esm/src/instances/routes.d.ts +7 -0
  161. package/dist/esm/src/instances/routes.js +179 -0
  162. package/dist/esm/src/middleware/cors.d.ts +6 -0
  163. package/dist/esm/src/middleware/cors.js +14 -0
  164. package/dist/esm/src/middleware/kapeta.d.ts +15 -0
  165. package/dist/esm/src/middleware/kapeta.js +28 -0
  166. package/dist/esm/src/middleware/stringBody.d.ts +9 -0
  167. package/dist/esm/src/middleware/stringBody.js +18 -0
  168. package/dist/esm/src/networkManager.d.ts +37 -0
  169. package/dist/esm/src/networkManager.js +119 -0
  170. package/dist/esm/src/operatorManager.d.ts +41 -0
  171. package/dist/esm/src/operatorManager.js +211 -0
  172. package/dist/esm/src/progressListener.d.ts +31 -0
  173. package/dist/esm/src/progressListener.js +133 -0
  174. package/dist/esm/src/providerManager.d.ts +11 -0
  175. package/dist/esm/src/providerManager.js +84 -0
  176. package/dist/esm/src/providers/routes.d.ts +7 -0
  177. package/dist/esm/src/providers/routes.js +46 -0
  178. package/dist/esm/src/proxy/routes.d.ts +7 -0
  179. package/dist/esm/src/proxy/routes.js +115 -0
  180. package/dist/esm/src/proxy/types/rest.d.ts +10 -0
  181. package/dist/esm/src/proxy/types/rest.js +123 -0
  182. package/dist/esm/src/proxy/types/web.d.ts +8 -0
  183. package/dist/esm/src/proxy/types/web.js +61 -0
  184. package/dist/esm/src/repositoryManager.d.ts +35 -0
  185. package/dist/esm/src/repositoryManager.js +247 -0
  186. package/dist/esm/src/serviceManager.d.ts +36 -0
  187. package/dist/esm/src/serviceManager.js +106 -0
  188. package/dist/esm/src/socketManager.d.ts +32 -0
  189. package/dist/esm/src/socketManager.js +125 -0
  190. package/dist/esm/src/storageService.d.ts +21 -0
  191. package/dist/esm/src/storageService.js +81 -0
  192. package/dist/esm/src/taskManager.d.ts +70 -0
  193. package/dist/esm/src/taskManager.js +181 -0
  194. package/dist/esm/src/tasks/routes.d.ts +7 -0
  195. package/dist/esm/src/tasks/routes.js +39 -0
  196. package/dist/esm/src/traffic/routes.d.ts +7 -0
  197. package/dist/esm/src/traffic/routes.js +22 -0
  198. package/dist/esm/src/types.d.ts +99 -0
  199. package/dist/esm/src/types.js +39 -0
  200. package/dist/esm/src/utils/BlockInstanceRunner.d.ts +28 -0
  201. package/dist/esm/src/utils/BlockInstanceRunner.js +432 -0
  202. package/dist/esm/src/utils/DefaultProviderInstaller.d.ts +15 -0
  203. package/dist/esm/src/utils/DefaultProviderInstaller.js +136 -0
  204. package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
  205. package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
  206. package/dist/esm/src/utils/LogData.d.ts +23 -0
  207. package/dist/esm/src/utils/LogData.js +46 -0
  208. package/dist/esm/src/utils/commandLineUtils.d.ts +8 -0
  209. package/dist/esm/src/utils/commandLineUtils.js +39 -0
  210. package/dist/esm/src/utils/pathTemplateParser.d.ts +30 -0
  211. package/dist/esm/src/utils/pathTemplateParser.js +135 -0
  212. package/dist/esm/src/utils/utils.d.ts +40 -0
  213. package/dist/esm/src/utils/utils.js +148 -0
  214. package/dist/esm/start.d.ts +5 -0
  215. package/dist/esm/start.js +17 -0
  216. package/dist/esm/test/proxy/types/rest.test.d.ts +5 -0
  217. package/dist/esm/test/proxy/types/rest.test.js +48 -0
  218. package/dist/esm/test/utils/pathTemplateParser.test.d.ts +5 -0
  219. package/dist/esm/test/utils/pathTemplateParser.test.js +27 -0
  220. package/index.ts +280 -0
  221. package/jest.config.js +8 -0
  222. package/package.json +134 -0
  223. package/src/RepositoryWatcher.ts +363 -0
  224. package/src/ai/aiClient.ts +93 -0
  225. package/src/ai/routes.ts +39 -0
  226. package/src/ai/transform.ts +275 -0
  227. package/src/ai/types.ts +45 -0
  228. package/src/api.ts +32 -0
  229. package/src/assetManager.ts +355 -0
  230. package/src/assets/routes.ts +183 -0
  231. package/src/attachments/routes.ts +79 -0
  232. package/src/authManager.ts +67 -0
  233. package/src/cacheManager.ts +59 -0
  234. package/src/clusterService.ts +142 -0
  235. package/src/codeGeneratorManager.ts +109 -0
  236. package/src/config/routes.ts +201 -0
  237. package/src/configManager.ts +180 -0
  238. package/src/containerManager.ts +1178 -0
  239. package/src/definitionsManager.ts +212 -0
  240. package/src/filesystem/routes.ts +123 -0
  241. package/src/filesystemManager.ts +133 -0
  242. package/src/identities/routes.ts +38 -0
  243. package/src/instanceManager.ts +1160 -0
  244. package/src/instances/routes.ts +203 -0
  245. package/src/middleware/cors.ts +14 -0
  246. package/src/middleware/kapeta.ts +41 -0
  247. package/src/middleware/stringBody.ts +21 -0
  248. package/src/networkManager.ts +148 -0
  249. package/src/operatorManager.ts +294 -0
  250. package/src/progressListener.ts +151 -0
  251. package/src/providerManager.ts +97 -0
  252. package/src/providers/routes.ts +51 -0
  253. package/src/proxy/routes.ts +153 -0
  254. package/src/proxy/types/rest.ts +172 -0
  255. package/src/proxy/types/web.ts +70 -0
  256. package/src/repositoryManager.ts +291 -0
  257. package/src/serviceManager.ts +133 -0
  258. package/src/socketManager.ts +138 -0
  259. package/src/storageService.ts +97 -0
  260. package/src/taskManager.ts +247 -0
  261. package/src/tasks/routes.ts +43 -0
  262. package/src/traffic/routes.ts +23 -0
  263. package/src/types.ts +112 -0
  264. package/src/utils/BlockInstanceRunner.ts +577 -0
  265. package/src/utils/DefaultProviderInstaller.ts +150 -0
  266. package/src/utils/InternalConfigProvider.ts +214 -0
  267. package/src/utils/LogData.ts +50 -0
  268. package/src/utils/commandLineUtils.ts +45 -0
  269. package/src/utils/pathTemplateParser.ts +157 -0
  270. package/src/utils/utils.ts +155 -0
  271. package/start.ts +14 -0
  272. package/test/proxy/types/rest.test.ts +54 -0
  273. package/test/utils/pathTemplateParser.test.ts +29 -0
  274. package/tsconfig.json +15 -0
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import chokidar, { FSWatcher } from 'chokidar';
7
+ import ClusterConfiguration, { Definition, DefinitionInfo } from '@kapeta/local-cluster-config';
8
+ import FS from 'fs-extra';
9
+ import Path from 'node:path';
10
+ import YAML from 'yaml';
11
+ import { parseKapetaUri } from '@kapeta/nodejs-utils';
12
+ import _ from 'lodash';
13
+ import { socketManager } from './socketManager';
14
+ import { SourceOfChange, WatchEventName } from './types';
15
+ import { cacheManager } from './cacheManager';
16
+ import { EventEmitter } from 'node:events';
17
+ import { assetManager } from './assetManager';
18
+ import { definitionsManager } from './definitionsManager';
19
+
20
+ interface AssetIdentity {
21
+ handle: string;
22
+ name: string;
23
+ version: string;
24
+ }
25
+ const KAPETA_YML_RX = /^kapeta.ya?ml$/;
26
+
27
+ let definitions: DefinitionInfo[] | undefined;
28
+ let definitionTimeout: NodeJS.Timeout | undefined;
29
+
30
+ function getDefinitionsDebounced() {
31
+ if (definitionTimeout) {
32
+ clearTimeout(definitionTimeout);
33
+ definitionTimeout = undefined;
34
+ }
35
+ if (!definitions) {
36
+ definitions = ClusterConfiguration.getDefinitions();
37
+ } else {
38
+ definitionTimeout = setTimeout(() => {
39
+ definitionTimeout = undefined;
40
+ definitions = undefined;
41
+ }, 500);
42
+ }
43
+
44
+ return definitions;
45
+ }
46
+
47
+ export class RepositoryWatcher extends EventEmitter {
48
+ private watcher?: FSWatcher;
49
+ private disabled: boolean = false;
50
+ private readonly baseDir: string;
51
+ private allDefinitions: DefinitionInfo[] = [];
52
+ private symbolicLinks: { [link: string]: string } = {};
53
+ private sourceOfChange: Map<string, SourceOfChange> = new Map();
54
+ constructor() {
55
+ super();
56
+ this.baseDir = ClusterConfiguration.getRepositoryBasedir();
57
+ }
58
+
59
+ setDisabled(disabled: boolean) {
60
+ this.disabled = disabled;
61
+ }
62
+ public watch() {
63
+ if (!FS.existsSync(this.baseDir)) {
64
+ FS.mkdirpSync(this.baseDir);
65
+ }
66
+
67
+ this.allDefinitions = ClusterConfiguration.getDefinitions();
68
+
69
+ try {
70
+ this.watcher = chokidar.watch(this.baseDir, {
71
+ followSymlinks: false,
72
+ ignorePermissionErrors: true,
73
+ disableGlobbing: true,
74
+ persistent: true,
75
+ depth: 2,
76
+ ignored: (path) => this.ignoreFile(path),
77
+ });
78
+ this.watcher.on('all', this.handleFileChange.bind(this));
79
+ this.watcher.on('error', (error) => {
80
+ console.log('Error watching repository', error);
81
+ });
82
+ this.watcher.on('ready', () => {
83
+ console.log('Watching local repository for provider changes: %s', this.baseDir);
84
+ });
85
+ } catch (e) {
86
+ // Fallback to run without watch mode due to potential platform issues.
87
+ // https://nodejs.org/docs/latest/api/fs.html#caveats
88
+ console.log('Unable to watch for changes. Changes to assets will not update automatically.', e);
89
+ return;
90
+ }
91
+ }
92
+
93
+ async setSourceOfChangeFor(file: string, source: SourceOfChange) {
94
+ this.sourceOfChange.set(file, source);
95
+ try {
96
+ const realPath = await FS.realpath(file);
97
+ if (realPath !== file) {
98
+ this.sourceOfChange.set(realPath, source);
99
+ }
100
+ } catch (e) {
101
+ // Ignore
102
+ }
103
+ }
104
+
105
+ async clearSourceOfChangeFor(file: string) {
106
+ this.sourceOfChange.delete(file);
107
+ try {
108
+ const realPath = await FS.realpath(file);
109
+ if (realPath !== file) {
110
+ this.sourceOfChange.delete(realPath);
111
+ }
112
+ } catch (e) {
113
+ // Ignore
114
+ }
115
+ }
116
+
117
+ public async unwatch() {
118
+ if (!this.watcher) {
119
+ return;
120
+ }
121
+ this.symbolicLinks = {};
122
+ await this.watcher.close();
123
+ this.watcher = undefined;
124
+ }
125
+
126
+ private async getAssetIdentity(path: string): Promise<AssetIdentity | undefined> {
127
+ const baseName = Path.basename(path);
128
+ let handle, name, version;
129
+ if (path.startsWith(this.baseDir)) {
130
+ const relativePath = Path.relative(this.baseDir, path);
131
+ // Inside the repo we can use the path to determine the handle, name and version
132
+ [handle, name, version] = relativePath.split(Path.sep);
133
+ if (!handle || !name || !version) {
134
+ // Do nothing with this
135
+ return;
136
+ }
137
+
138
+ return {
139
+ handle,
140
+ name,
141
+ version,
142
+ };
143
+ }
144
+
145
+ if (!KAPETA_YML_RX.test(baseName)) {
146
+ // Do nothing with this
147
+ return;
148
+ }
149
+ // Outside the repo we need to use the file content to determine the handle, name
150
+ // Version is always 'local'
151
+ version = 'local';
152
+
153
+ try {
154
+ const definition: Definition = YAML.parse((await FS.readFile(path)).toString());
155
+ const uri = parseKapetaUri(definition.metadata.name);
156
+ handle = uri.handle;
157
+ name = uri.name;
158
+ return {
159
+ handle,
160
+ name,
161
+ version,
162
+ };
163
+ } catch (e) {
164
+ // Ignore issues in the YML file
165
+ return;
166
+ }
167
+ }
168
+
169
+ private async handleFileChange(eventName: WatchEventName, path: string) {
170
+ if (!path) {
171
+ return;
172
+ }
173
+
174
+ if (this.disabled) {
175
+ return;
176
+ }
177
+
178
+ const assetIdentity = await this.getAssetIdentity(path);
179
+ if (!assetIdentity) {
180
+ return;
181
+ }
182
+
183
+ // If this is false it's because we're watching a symlink target
184
+ const withinRepo = path.startsWith(this.baseDir);
185
+ if (withinRepo && assetIdentity.version === 'local' && path.endsWith(Path.sep + 'local')) {
186
+ // This is likely a symlink target
187
+ if (eventName === 'add') {
188
+ //console.log('Checking if we should add symlink target', handle, name, version, path);
189
+ await this.addSymlinkTarget(path);
190
+ }
191
+
192
+ if (eventName === 'unlink') {
193
+ await this.removeSymlinkTarget(path);
194
+ }
195
+
196
+ if (eventName === 'change') {
197
+ await this.updateSymlinkTarget(path);
198
+ }
199
+ }
200
+
201
+ const repoPath = this.getRepositoryPath(assetIdentity);
202
+
203
+ if (
204
+ eventName === 'change' &&
205
+ !withinRepo &&
206
+ assetIdentity.version === 'local' &&
207
+ Path.basename(path) === 'kapeta.yml' &&
208
+ (await this.exists(path)) &&
209
+ !(await this.exists(repoPath))
210
+ ) {
211
+ // This happens when a local asset is renamed
212
+ const oldPath = _.findKey(this.symbolicLinks, (link) => link === path);
213
+ if (oldPath) {
214
+ await FS.unlink(oldPath);
215
+ await assetManager.importFile(path);
216
+ return;
217
+ }
218
+ }
219
+
220
+ const sourceOfChange = this.sourceOfChange.get(path) ?? 'filesystem';
221
+ await this.checkForChange(assetIdentity, sourceOfChange);
222
+
223
+ // We consume the sourceOfChange when the file is changed
224
+ this.sourceOfChange.delete(path);
225
+ }
226
+
227
+ private getRepositoryPath(assetIdentity: AssetIdentity) {
228
+ return Path.join(this.baseDir, assetIdentity.handle, assetIdentity.name, assetIdentity.version);
229
+ }
230
+
231
+ private async checkForChange(assetIdentity: AssetIdentity, sourceOfChange: SourceOfChange) {
232
+ const ymlPath = Path.join(this.getRepositoryPath(assetIdentity), 'kapeta.yml');
233
+ const newDefinitions = getDefinitionsDebounced();
234
+
235
+ const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
236
+ let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
237
+ const ymlExists = await this.exists(ymlPath);
238
+
239
+ let type;
240
+ if (ymlExists) {
241
+ if (currentDefinition) {
242
+ if (newDefinition && _.isEqual(currentDefinition, newDefinition)) {
243
+ //Definition was not changed
244
+ return;
245
+ }
246
+ type = 'updated';
247
+ } else if (newDefinition) {
248
+ type = 'added';
249
+ currentDefinition = newDefinition;
250
+ } else {
251
+ //Other definition was added / updated - ignore
252
+ return;
253
+ }
254
+ } else {
255
+ if (currentDefinition) {
256
+ //Something was removed
257
+ type = 'removed';
258
+ } else {
259
+ //Other definition was removed - ignore
260
+ return;
261
+ }
262
+ }
263
+
264
+ const payload = {
265
+ type,
266
+ definition: newDefinition?.definition ?? currentDefinition?.definition,
267
+ asset: assetIdentity,
268
+ sourceOfChange,
269
+ };
270
+
271
+ this.allDefinitions = newDefinitions;
272
+
273
+ //console.log('Asset changed', payload);
274
+ socketManager.emitGlobal('asset-change', payload);
275
+ this.emit('change', payload);
276
+
277
+ cacheManager.flush();
278
+ }
279
+
280
+ private async exists(path: string): Promise<boolean> {
281
+ try {
282
+ await FS.access(path);
283
+ return true;
284
+ } catch (e) {
285
+ return false;
286
+ }
287
+ }
288
+ private async removeSymlinkTarget(path: string) {
289
+ if (this.symbolicLinks[path]) {
290
+ //console.log('Unwatching symlink target %s => %s', path, this.symbolicLinks[path]);
291
+ this.watcher?.unwatch(this.symbolicLinks[path]);
292
+ delete this.symbolicLinks[path];
293
+ }
294
+ }
295
+
296
+ private async updateSymlinkTarget(path: string) {
297
+ if (this.symbolicLinks[path]) {
298
+ //console.log('Updating symlink target %s => %s', path, this.symbolicLinks[path]);
299
+ this.watcher?.unwatch(this.symbolicLinks[path]);
300
+ delete this.symbolicLinks[path];
301
+ await this.addSymlinkTarget(path);
302
+ }
303
+ }
304
+
305
+ private async addSymlinkTarget(path: string) {
306
+ try {
307
+ // Make sure we're not watching the symlink target
308
+ await this.removeSymlinkTarget(path);
309
+ let symbolicLink = false;
310
+ try {
311
+ const stat = await FS.lstat(path);
312
+ symbolicLink = stat.isSymbolicLink();
313
+ } catch (e) {}
314
+
315
+ if (symbolicLink) {
316
+ try {
317
+ const realPath = Path.join(await FS.realpath(path), 'kapeta.yml');
318
+ if (await this.exists(realPath)) {
319
+ //console.log('Watching symlink target %s => %s', path, realPath);
320
+ this.watcher?.add(realPath);
321
+ this.symbolicLinks[path] = realPath;
322
+ }
323
+ } catch (e) {
324
+ // Remove the symlink - it's broken
325
+ await FS.remove(path);
326
+ }
327
+ }
328
+ } catch (e) {
329
+ // Ignore
330
+ console.warn('Failed to check local symlink target', e);
331
+ }
332
+ }
333
+
334
+ private ignoreFile(path: string) {
335
+ if (!path.startsWith(this.baseDir)) {
336
+ return false;
337
+ }
338
+ if (path.includes(Path.sep + 'node_modules' + Path.sep)) {
339
+ return true;
340
+ }
341
+
342
+ const filename = Path.basename(path);
343
+ if (filename.startsWith('.')) {
344
+ return true;
345
+ }
346
+
347
+ const relativePath = Path.relative(this.baseDir, path).split(Path.sep);
348
+
349
+ try {
350
+ if (FS.statSync(path).isDirectory()) {
351
+ if (relativePath.length > 3) {
352
+ return true;
353
+ }
354
+ return false;
355
+ }
356
+ } catch (e) {
357
+ // Didn't exist - dont ignore
358
+ return false;
359
+ }
360
+
361
+ return !/^kapeta\.ya?ml$/.test(filename);
362
+ }
363
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+ import request from 'request';
6
+ import { PlanContext, transformToPlan } from './transform';
7
+ import { Application } from './types';
8
+ import { KapetaAPI } from '@kapeta/nodejs-api-client';
9
+ import ClusterConfiguration from '@kapeta/local-cluster-config';
10
+ import { getRemoteUrl } from '../utils/utils';
11
+
12
+ export type PromptResult = {
13
+ explanation: string;
14
+ response: string;
15
+ context?: PlanContext;
16
+ };
17
+
18
+ export interface AIMessage {
19
+ content: string;
20
+ role: 'user' | 'assistant';
21
+ }
22
+
23
+ export interface AIRequest {
24
+ messages: AIMessage[];
25
+ }
26
+
27
+ class AIClient {
28
+ private readonly _baseUrl: string;
29
+
30
+ constructor() {
31
+ this._baseUrl = getRemoteUrl('ai-service', 'https://ai.kapeta.com');
32
+ }
33
+
34
+ public async sendPrompt(handle: string, body: AIRequest): Promise<PromptResult> {
35
+ const url = `${this._baseUrl}/v1/plan?type=chat`;
36
+
37
+ const headers: { [k: string]: string } = {};
38
+ const api = new KapetaAPI();
39
+ if (api.hasToken()) {
40
+ headers['Authorization'] = `Bearer ${await api.getAccessToken()}`;
41
+ }
42
+
43
+ const options = {
44
+ url,
45
+ method: 'POST',
46
+ json: true,
47
+ body,
48
+ headers,
49
+ };
50
+
51
+ return new Promise((resolve, reject) => {
52
+ request(options, async (error, response, application: Application) => {
53
+ if (error) {
54
+ console.error(error);
55
+ reject(error);
56
+ }
57
+
58
+ if (response.statusCode !== 200) {
59
+ console.log('Prompt failed', response.statusCode, response.body);
60
+ reject(new Error(`Invalid response code: ${response.statusCode}`));
61
+ return;
62
+ }
63
+
64
+ try {
65
+ if (application?.name) {
66
+ const planContext = await transformToPlan(handle, application);
67
+ resolve({
68
+ explanation: application.explanation,
69
+ response: application.response ?? application.explanation ?? 'Plan was generated',
70
+ context: planContext,
71
+ });
72
+ } else {
73
+ resolve({
74
+ explanation: application.explanation,
75
+ response:
76
+ application.response ??
77
+ application.explanation ??
78
+ 'I did not understand your request. Please rephrase.',
79
+ });
80
+ }
81
+ } catch (err: any) {
82
+ console.error(err);
83
+ resolve({
84
+ explanation: '',
85
+ response: 'I did not understand your request. Please rephrase.',
86
+ });
87
+ }
88
+ });
89
+ });
90
+ }
91
+ }
92
+
93
+ export const aiClient = new AIClient();
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import Router from 'express-promise-router';
7
+ import { Response } from 'express';
8
+
9
+ import { corsHandler } from '../middleware/cors';
10
+
11
+ import { stringBody, StringBodyRequest } from '../middleware/stringBody';
12
+ import { aiClient, AIRequest } from './aiClient';
13
+ import { KapetaBodyRequest } from '../types';
14
+ import YAML from 'yaml';
15
+
16
+ const router = Router();
17
+
18
+ router.use('/', corsHandler);
19
+ router.use('/', stringBody);
20
+
21
+ router.post('/prompt/:handle', async (req: KapetaBodyRequest, res: Response) => {
22
+ const handle = req.params.handle;
23
+ try {
24
+ const aiRequest: AIRequest = JSON.parse(req.stringBody ?? '{}');
25
+ const result = await aiClient.sendPrompt(handle, aiRequest);
26
+ if (req.accepts('application/yaml')) {
27
+ res.set('Content-Type', 'application/yaml');
28
+ res.send(YAML.stringify(result));
29
+ } else {
30
+ res.json(result);
31
+ }
32
+ } catch (err: any) {
33
+ console.error('Failed to send prompt', err);
34
+ res.status(400).send({ error: err.message });
35
+ return;
36
+ }
37
+ });
38
+
39
+ export default router;