@vida-global/apps-tools 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +23 -0
- package/lib/actionHandlers/appFunctionHandler.js +7 -0
- package/lib/actionHandlers/appHooksHandler.js +7 -0
- package/lib/actionHandlers/index.js +4 -0
- package/lib/appResponses/appFunctionResponse.js +23 -0
- package/lib/appResponses/appHookResponse.js +21 -0
- package/lib/appResponses/calendarAppResponse.js +6 -0
- package/lib/appResponses/index.js +9 -0
- package/lib/apps/appManager/abstractAppManager.js +70 -0
- package/lib/apps/appManager/appServerAppManager.js +237 -0
- package/lib/apps/appManager/vaderApiClient.js +81 -0
- package/lib/apps/appManager/vidaLiveAppManager.js +187 -0
- package/lib/apps/vidaApp.js +345 -0
- package/lib/errors/illegalAppInvocationError.js +8 -0
- package/lib/openApi/baseApiSpec.js +10 -0
- package/lib/openApi/index.js +150 -0
- package/lib/providers/provider.js +141 -0
- package/lib/providers/providerManager.js +73 -0
- package/lib/providers/providers/auth/generic/index.js +60 -0
- package/lib/providers/providers/auth/google/index.js +251 -0
- package/lib/providers/providers/auth/hubspot/index.js +313 -0
- package/lib/providers/providers/auth/mcp/index.js +99 -0
- package/lib/providers/providers/auth/outlook/index.js +257 -0
- package/lib/providers/providers/auth/squareUp/index.js +111 -0
- package/lib/providers/providers/mock.js +51 -0
- package/lib/server/appController.js +112 -0
- package/lib/server/server.js +73 -0
- package/lib/userContext.js +218 -0
- package/package.json +33 -0
package/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { AppHooksHandler, AppFunctionHandler } = require("./lib/actionHandlers");
|
|
2
|
+
const { AppFunctionResponse, AppHookResponse } = require('./lib/appResponses');
|
|
3
|
+
const { BaseProviderConfigurator, BaseProvider } = require('./lib/providers/provider');
|
|
4
|
+
const { AppServer } = require("./lib/server/server");
|
|
5
|
+
const { VaderUserContext } = require("./lib/userContext");
|
|
6
|
+
const { VidaApp } = require("./lib/apps/vidaApp");
|
|
7
|
+
const { VidaLiveAppManager } = require("./lib/apps/appManager/vidaLiveAppManager");
|
|
8
|
+
const { HttpClient, logger } = require("@vida-global/core");
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
AppFunctionHandler,
|
|
12
|
+
AppFunctionResponse,
|
|
13
|
+
AppHooksHandler,
|
|
14
|
+
AppHookResponse,
|
|
15
|
+
AppServer,
|
|
16
|
+
BaseProvider,
|
|
17
|
+
BaseProviderConfigurator,
|
|
18
|
+
HttpClient,
|
|
19
|
+
logger,
|
|
20
|
+
VaderUserContext,
|
|
21
|
+
VidaApp,
|
|
22
|
+
VidaLiveAppManager
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class AppFunctionResponse {
|
|
2
|
+
status = null;
|
|
3
|
+
data = null;
|
|
4
|
+
error = null;
|
|
5
|
+
message = null;
|
|
6
|
+
functionName = null;
|
|
7
|
+
functionArgs = null;
|
|
8
|
+
responseTime = null;
|
|
9
|
+
|
|
10
|
+
constructor () {
|
|
11
|
+
this.logger = console
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
log () {
|
|
15
|
+
if (this.status === 'error') {
|
|
16
|
+
this.logger.error(`App response error: ${this.status} - ${this.message}`)
|
|
17
|
+
} else {
|
|
18
|
+
this.logger.log(`App response: ${this.status}`)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { AppFunctionResponse };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class AppHookResponse {
|
|
2
|
+
status = null;
|
|
3
|
+
error = null;
|
|
4
|
+
data = null;
|
|
5
|
+
message = null;
|
|
6
|
+
responseTime = null;
|
|
7
|
+
|
|
8
|
+
constructor () {
|
|
9
|
+
this.logger = console
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
log () {
|
|
13
|
+
if (this.status === 'error') {
|
|
14
|
+
this.logger.error(`App response error: ${this.status} - ${this.message}`)
|
|
15
|
+
} else {
|
|
16
|
+
this.logger.log(`App response: ${this.status}`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { AppHookResponse };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const { AppFunctionResponse } = require('./appFunctionResponse');
|
|
2
|
+
const { AppHookResponse } = require('./appHookResponse');
|
|
3
|
+
const { CalendarAppResponse } = require('./calendarAppResponse');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
AppFunctionResponse,
|
|
7
|
+
AppHookResponse,
|
|
8
|
+
CalendarAppResponse
|
|
9
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { logger } = require('@vida-global/core');
|
|
2
|
+
const { ProviderManager } = require('../../providers/providerManager');
|
|
3
|
+
const { VaderUserContextProxy } = require('../../userContext')
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AbstractAppManager {
|
|
7
|
+
#providerManager;
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
context(ctx) {
|
|
11
|
+
return new VaderUserContextProxy(this, ctx)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
get logger() {
|
|
16
|
+
return logger;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
get attachedToAppServer() {
|
|
21
|
+
return this.constructor.name == 'AppServerAppManager';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
get providerManager() {
|
|
26
|
+
if (!this.#providerManager) {
|
|
27
|
+
this.#providerManager = new ProviderManager(this.redisClient, this.attachedToAppServer);
|
|
28
|
+
}
|
|
29
|
+
return this.#providerManager;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async _getApp(appClass, appId, appVersion, appManifest, userContext) {
|
|
34
|
+
const providerManager = this.providerManager;
|
|
35
|
+
const isServer = this.attachedToAppServer;
|
|
36
|
+
return await appClass.get(appClass,
|
|
37
|
+
{ appId,
|
|
38
|
+
appVersion,
|
|
39
|
+
appManifest,
|
|
40
|
+
userContext,
|
|
41
|
+
providerManager },
|
|
42
|
+
{ isServer });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/***********************************************************************************************
|
|
47
|
+
* MISC
|
|
48
|
+
***********************************************************************************************/
|
|
49
|
+
debugPrintCalls(appId, appVersion, functionName, callContext, manifest, functionArgs) {
|
|
50
|
+
this.logger.info(`Handling call ${appId} ${appVersion} ${functionName}`)
|
|
51
|
+
|
|
52
|
+
// User: ${JSON.stringify(callContext.user, null, 2)}
|
|
53
|
+
// Agent: ${JSON.stringify(callContext.agent, null, 2)}
|
|
54
|
+
// Caller: ${JSON.stringify(callContext.caller, null, 2)}
|
|
55
|
+
|
|
56
|
+
this.logger.info(
|
|
57
|
+
`Function call details:
|
|
58
|
+
Request Sent Dependencies: ${JSON.stringify(callContext?.manifest?.dependencies, null, 2)}
|
|
59
|
+
App ID: ${appId}
|
|
60
|
+
App Version: ${appVersion}
|
|
61
|
+
Function name: ${functionName}
|
|
62
|
+
Function args: ${JSON.stringify(functionArgs, null, 2)}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
module.exports = {
|
|
69
|
+
AbstractAppManager
|
|
70
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
const { AbstractAppManager } = require('./abstractAppManager');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { generateOpenApiSpec } = require('../../openApi');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const _ = require("lodash")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AppServerAppManager extends AbstractAppManager {
|
|
9
|
+
#apps;
|
|
10
|
+
#useMocks;
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
constructor({ apps, useMocks }) {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.#useMocks = useMocks;
|
|
16
|
+
this.#apps = apps.map(app => {
|
|
17
|
+
const [id, version] = app.split('/')
|
|
18
|
+
return {id, version}
|
|
19
|
+
})
|
|
20
|
+
this.#validateApps();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
get apps() {
|
|
25
|
+
return structuredClone(this.#apps);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
get appsDirectory() {
|
|
30
|
+
return `${process.cwd()}/apps`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/***********************************************************************************************
|
|
35
|
+
* APP MANAGEMENT
|
|
36
|
+
***********************************************************************************************/
|
|
37
|
+
#mergeMocks(appId, appVersion, appManifest) {
|
|
38
|
+
if (!this.#useMocks) return;
|
|
39
|
+
try {
|
|
40
|
+
const mockData = require(`${process.cwd()}/apps/${appId}/${appVersion}/test/mocks.js`)
|
|
41
|
+
appManifest.dependencies = _.merge(_.cloneDeep(appManifest.dependencies), mockData)
|
|
42
|
+
} catch (error) {
|
|
43
|
+
this.logger.warn(`No mocks found for app ${appId} ${appVersion} ${error}`)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async getApp(appId, appVersion, appManifest, userContext) {
|
|
49
|
+
if (!appManifest) {
|
|
50
|
+
this.logger.info('no manifest provided, getting vanilla manifest')
|
|
51
|
+
appManifest = this.fetchVanillaAppManifest(appId, appVersion, userContext)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.#mergeMocks(appId, appVersion, appManifest);
|
|
55
|
+
|
|
56
|
+
const appClass = require(`${process.cwd()}/apps/${appId}/${appVersion}/app.js`);
|
|
57
|
+
return await this._getApp(appClass, appId, appVersion, appManifest, userContext);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
fetchVanillaAppManifest(appId, appVersion) {
|
|
62
|
+
const manifest = require(`${process.cwd()}/apps/${appId}/${appVersion}/manifest.js`)
|
|
63
|
+
// Return a deep clone to avoid mutating the cached module object
|
|
64
|
+
return _.cloneDeep(manifest)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async fetchAllVanillaManifests(userContext = null) {
|
|
69
|
+
const manifests = {}
|
|
70
|
+
for (const {id, version} of this.apps) {
|
|
71
|
+
const appIdVersion = `${id}:${version}`
|
|
72
|
+
const manifest = require(`${process.cwd()}/apps/${id}/${version}/manifest.js`)
|
|
73
|
+
// Deep clone to avoid mutating the cached module export
|
|
74
|
+
manifests[appIdVersion] = _.cloneDeep(manifest)
|
|
75
|
+
}
|
|
76
|
+
return manifests
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/***********************************************************************************************
|
|
81
|
+
* VALIDATION
|
|
82
|
+
***********************************************************************************************/
|
|
83
|
+
#validateApps() {
|
|
84
|
+
for (const {id, version} of this.apps) {
|
|
85
|
+
this.#validateApp(id, version);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
#validateApp(appId, version) {
|
|
91
|
+
const appPath = path.join(this.appsDirectory, appId, version);
|
|
92
|
+
|
|
93
|
+
// Check if directory exists
|
|
94
|
+
if (!fs.existsSync(appPath)) {
|
|
95
|
+
throw new Error(`Invalid app structure: Missing directory at ${appPath}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check for app.js file existence
|
|
99
|
+
const appJsPath = path.join(appPath, 'app.js');
|
|
100
|
+
if (!fs.existsSync(appJsPath)) {
|
|
101
|
+
throw new Error(`Invalid app structure: Missing app.js for ${appId}/${version} in ${appPath}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check for manifest.js file existence
|
|
105
|
+
const manifestJsPath = path.join(appPath, 'manifest.js');
|
|
106
|
+
if (!fs.existsSync(manifestJsPath)) {
|
|
107
|
+
throw new Error(`Invalid app structure: Missing manifest.js for ${appId}/${version} in ${appPath}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
/***********************************************************************************************
|
|
113
|
+
* OPEN API
|
|
114
|
+
***********************************************************************************************/
|
|
115
|
+
async generateOpenApiSpec () {
|
|
116
|
+
return await generateOpenApiSpec(this);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/***********************************************************************************************
|
|
121
|
+
* CALL HANDLERS
|
|
122
|
+
***********************************************************************************************/
|
|
123
|
+
async handleFunctionCall(appId, appVersion, appManifest, functionName, functionArgs, userContext) {
|
|
124
|
+
const manifest = appManifest
|
|
125
|
+
|
|
126
|
+
this.debugPrintCalls(appId,
|
|
127
|
+
appVersion,
|
|
128
|
+
functionName,
|
|
129
|
+
userContext.toDict(),
|
|
130
|
+
manifest,
|
|
131
|
+
functionArgs);
|
|
132
|
+
|
|
133
|
+
this.logger.info(`Handling function call ${appId} ${appVersion} ${functionName}`)
|
|
134
|
+
const app = await this.getApp(appId, appVersion, manifest, userContext);
|
|
135
|
+
const hasFunction = await app.isFunctionInApp(functionName)
|
|
136
|
+
|
|
137
|
+
if (!hasFunction) throw new Error(`Function not found: ${functionName}`);
|
|
138
|
+
|
|
139
|
+
const result = await app.handleFunctionCall(functionName, functionArgs)
|
|
140
|
+
this.logger.info(`Function result: ${JSON.stringify(result, null, 2)}`)
|
|
141
|
+
return result
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
async handleHookCall(appId, appVersion, appManifest, hookName, hookArgs, userContext) {
|
|
146
|
+
let manifest = appManifest
|
|
147
|
+
this.debugPrintCalls(appId,
|
|
148
|
+
appVersion,
|
|
149
|
+
hookName,
|
|
150
|
+
userContext.toDict(),
|
|
151
|
+
manifest,
|
|
152
|
+
hookArgs);
|
|
153
|
+
|
|
154
|
+
this.logger.info(`Handling hook call ${appId} ${appVersion} ${hookName}`)
|
|
155
|
+
const app = await this.getApp(appId, appVersion, manifest, userContext);
|
|
156
|
+
const hasHook = await app.isHookInApp(hookName)
|
|
157
|
+
|
|
158
|
+
if (!hasHook) throw new Error(`Hook not found: ${hookName}`);
|
|
159
|
+
|
|
160
|
+
const result = await app.handleHookCall(hookName, hookArgs);
|
|
161
|
+
this.logger.info(`Hook result: ${JSON.stringify(result, null, 2)}`);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
/***********************************************************************************************
|
|
167
|
+
* DATA FETCHERS
|
|
168
|
+
***********************************************************************************************/
|
|
169
|
+
async getFunctions(onlyEnabled=false, userContext) {
|
|
170
|
+
let appFunctions = []
|
|
171
|
+
const appVersionIds = await userContext.getAppVersionIds()
|
|
172
|
+
this.logger.info('VADER: getFunctions', appVersionIds)
|
|
173
|
+
|
|
174
|
+
for (const appVersionId of appVersionIds) {
|
|
175
|
+
const [appId, appVersion] = appVersionId.split(':')
|
|
176
|
+
const app = await this.getApp(appId, appVersion, null, userContext);
|
|
177
|
+
appFunctions = await app.getFunctions(onlyEnabled)
|
|
178
|
+
}
|
|
179
|
+
return appFunctions
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
async getHooks(userContext) {
|
|
184
|
+
const responses = []
|
|
185
|
+
const appVersionIds = await userContext.getAppVersionIds()
|
|
186
|
+
this.logger.info('VADER: getHooks', appVersionIds)
|
|
187
|
+
|
|
188
|
+
for (let appVersionId of appVersionIds) {
|
|
189
|
+
const [appId, appVersion] = appVersionId.split(':')
|
|
190
|
+
const vanillaManifest = this.fetchVanillaAppManifest(appId, appVersion, userContext);
|
|
191
|
+
const app = await this.getApp(appId, appVersion, vanillaManifest, userContext);
|
|
192
|
+
const appHooks = await app.getHooks()
|
|
193
|
+
|
|
194
|
+
for (const hook of appHooks) {
|
|
195
|
+
const hasHook = await app.isHookInApp(hook.name)
|
|
196
|
+
if (hasHook) {
|
|
197
|
+
const result = await app.handleHookCall(hook.name, {})
|
|
198
|
+
this.logger.info(`Hook result: ${JSON.stringify(result, null, 2)}`)
|
|
199
|
+
responses.push(result)
|
|
200
|
+
} else {
|
|
201
|
+
this.logger.warn('VADER: getHooks', hook.name, 'not found')
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
this.logger.info('VADER: getHooks', appVersionId)
|
|
206
|
+
const appInstructions = await app.getAgentInstructions()
|
|
207
|
+
if (appInstructions) {
|
|
208
|
+
const appFunctions = await this.getFunctions(true, userContext)
|
|
209
|
+
const functionNameString = _.map(appFunctions, f => f.name).join(', ')
|
|
210
|
+
responses.push({
|
|
211
|
+
status: 'success',
|
|
212
|
+
message: `Instructions for using ${functionNameString} functions: ${appInstructions}`
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return responses
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
async listApps(userContext) {
|
|
222
|
+
const apps = []
|
|
223
|
+
const allManifests = await this.fetchAllVanillaManifests(userContext)
|
|
224
|
+
|
|
225
|
+
for (const appIdVersion of Object.keys(allManifests)) {
|
|
226
|
+
const [appId, appVersion] = appIdVersion.split(':')
|
|
227
|
+
const vanillaManifest = this.fetchVanillaAppManifest(appId, appVersion, userContext)
|
|
228
|
+
const app = await this.getApp(appId, appVersion, vanillaManifest, userContext);
|
|
229
|
+
const compositeManifest = await app.getCompositeManifest()
|
|
230
|
+
apps.push(compositeManifest)
|
|
231
|
+
}
|
|
232
|
+
return apps
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
module.exports = { AppServerAppManager };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const { AppFunctionResponse } = require('../../appResponses/appFunctionResponse');
|
|
2
|
+
const { AppHookResponse } = require('../../appResponses/appHookResponse');
|
|
3
|
+
const { HttpClient } = require('@vida-global/core');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class VaderApiClient extends HttpClient {
|
|
7
|
+
#vaderServerUrl;
|
|
8
|
+
#vaderServerToken;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
constructor(vaderServerUrl, vaderServerToken) {
|
|
12
|
+
super();
|
|
13
|
+
this.#vaderServerUrl = vaderServerUrl;
|
|
14
|
+
this.#vaderServerToken = vaderServerToken;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async invokeAppManager(functionName, functionArgs = [], userContext = null){
|
|
19
|
+
const endpoint = `/invoke/apps/manager/${functionName}`
|
|
20
|
+
const body = {
|
|
21
|
+
userContext: userContext?.toDict() || null,
|
|
22
|
+
functionArgs,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const response = await this.post(endpoint, userContext, body);
|
|
26
|
+
return response.data;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async invokeFunction(app, functionName, functionArgs, userContext = null){
|
|
31
|
+
return await this.#invokeAction('function',
|
|
32
|
+
AppFunctionResponse,
|
|
33
|
+
app,
|
|
34
|
+
functionName,
|
|
35
|
+
functionArgs,
|
|
36
|
+
userContext);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async invokeHook(app, hookName, hookArgs, userContext = null) {
|
|
41
|
+
return await this.#invokeAction('hook',
|
|
42
|
+
AppHookResponse,
|
|
43
|
+
app,
|
|
44
|
+
hookName,
|
|
45
|
+
hookArgs,
|
|
46
|
+
userContext);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async #invokeAction(actionType, responseCls, app, actionName, actionArgs, userContext) {
|
|
51
|
+
const endpoint = `/invoke/app/${actionType}/${app.id()}/${app.version()}/${actionName}`
|
|
52
|
+
const body = {
|
|
53
|
+
userContext: userContext?.toDict() || null,
|
|
54
|
+
appManifest: await app.getCompositeManifest(),
|
|
55
|
+
actionArgs,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const response = await this.post(endpoint, userContext, body);
|
|
60
|
+
return response.data;
|
|
61
|
+
} catch(err) {
|
|
62
|
+
const appResponse = new responseCls();
|
|
63
|
+
appResponse.status = "error"
|
|
64
|
+
appResponse.message = await err.data();
|
|
65
|
+
return appResponse;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async post(endpoint, userContext, body) {
|
|
71
|
+
const vaderUrl = userContext?.get('customVaderUrl') || this.#vaderServerUrl;
|
|
72
|
+
const vaderToken = userContext?.get('customVaderToken') || this.#vaderServerToken;
|
|
73
|
+
const invokeUrl = `${vaderUrl}${endpoint}`;
|
|
74
|
+
const headers = {Authorization: `Bearer ${vaderToken}`};
|
|
75
|
+
|
|
76
|
+
return await super.post(invokeUrl, { body, headers });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
module.exports = { VaderApiClient };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
const { AbstractAppManager } = require('./abstractAppManager');
|
|
2
|
+
const { AppFunctionResponse } = require('../../appResponses/appFunctionResponse');
|
|
3
|
+
const { VaderApiClient } = require('./vaderApiClient');
|
|
4
|
+
const { VidaApp } = require('../vidaApp');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class VidaLiveAppManager extends AbstractAppManager {
|
|
8
|
+
#providersList;
|
|
9
|
+
#redisClient;
|
|
10
|
+
#vaderApiClient;
|
|
11
|
+
|
|
12
|
+
constructor({ redisClient, providersList, vaderServerUrl, vaderServerToken }) {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.#vaderApiClient = new VaderApiClient(vaderServerUrl, vaderServerToken);
|
|
15
|
+
this.#providersList = providersList;
|
|
16
|
+
this.#redisClient = redisClient;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
async initialize(server) {
|
|
21
|
+
await this.providerManager.buildRoutes(server, this.#providersList)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
get redisClient() {
|
|
26
|
+
return this.#redisClient;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
/***********************************************************************************************
|
|
31
|
+
* APP MANAGEMENT
|
|
32
|
+
***********************************************************************************************/
|
|
33
|
+
async getApp(appId, appVersion, appManifest, userContext) {
|
|
34
|
+
const serverManifest = await this.#vaderApiClient.invokeAppManager('getApp',
|
|
35
|
+
[appId, appVersion, appManifest],
|
|
36
|
+
userContext);
|
|
37
|
+
return await this._getApp(VidaApp, appId, appVersion, serverManifest, userContext);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
async fetchVanillaAppManifest(appId, appVersion) {
|
|
43
|
+
return await this.#vaderApiClient.invokeAppManager('fetchVanillaAppManifest',
|
|
44
|
+
[appId, appVersion],
|
|
45
|
+
userContext);
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async fetchAllVanillaManifests(userContext = null) {
|
|
51
|
+
return await this.#vaderApiClient.invokeAppManager('fetchAllVanillaManifests',
|
|
52
|
+
[],
|
|
53
|
+
userContext);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async installApp(appId, appVersion, manifest = null, userContext) {
|
|
58
|
+
if (await this.isAppInstalled(appId, appVersion, userContext)) {
|
|
59
|
+
throw new Error(`App already installed: ${appId} ${appVersion}`);
|
|
60
|
+
}
|
|
61
|
+
if (!manifest) {
|
|
62
|
+
manifest = await this.fetchVanillaAppManifest(appId, appVersion, userContext);
|
|
63
|
+
}
|
|
64
|
+
const app = await this.getApp(appId, appVersion, manifest,userContext);
|
|
65
|
+
await app.install(manifest)
|
|
66
|
+
return app
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
async updateInstalledApp(appId, appVersion, manifest, userContext) {
|
|
71
|
+
if (!await this.isAppInstalled(appId, appVersion, userContext)) {
|
|
72
|
+
throw new Error(`App not installed: ${appId} ${appVersion}`)
|
|
73
|
+
}
|
|
74
|
+
const vanillaManifest = await this.fetchVanillaAppManifest(appId, appVersion, userContext);
|
|
75
|
+
const app = await this.getApp(appId, appVersion, vanillaManifest,userContext);
|
|
76
|
+
await app.update(manifest);
|
|
77
|
+
return app
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async reloadInstalledApp(appId, appVersion, userContext) {
|
|
82
|
+
if (!await this.isAppInstalled(appId, appVersion, userContext)) {
|
|
83
|
+
throw new Error(`App not installed: ${appId} ${appVersion}`)
|
|
84
|
+
}
|
|
85
|
+
const vanillaManifest = await this.fetchVanillaAppManifest(appId, appVersion, userContext);
|
|
86
|
+
const app = await this.getApp(appId, appVersion, vanillaManifest,userContext);
|
|
87
|
+
await app.reload(vanillaManifest);
|
|
88
|
+
return app
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
async uninstallApp(appId, appVersion, userContext){
|
|
93
|
+
const vanillaManifest = await this.fetchVanillaAppManifest(appId, appVersion, userContext);
|
|
94
|
+
const app = await this.getApp(appId, appVersion, vanillaManifest, userContext);
|
|
95
|
+
|
|
96
|
+
if (!(await app.isInstalled())) return null;
|
|
97
|
+
|
|
98
|
+
this.logger.info(`Uninstalling app ${appId} ${appVersion}`)
|
|
99
|
+
await app.uninstall()
|
|
100
|
+
return app
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
async isAppInstalled(appId, appVersion, userContext) {
|
|
105
|
+
return await userContext.isAppInstalled(appId, appVersion)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/***********************************************************************************************
|
|
110
|
+
* INVOKERS
|
|
111
|
+
***********************************************************************************************/
|
|
112
|
+
async invokeFunction(functionName, functionArgs, userContext) {
|
|
113
|
+
const appVersionIds = await userContext.getAppVersionIds()
|
|
114
|
+
for (const appVersionId of appVersionIds) {
|
|
115
|
+
const [appId, appVersion] = appVersionId.split(':')
|
|
116
|
+
const app = await this.getApp(appId, appVersion, null, userContext);
|
|
117
|
+
|
|
118
|
+
if (!app.isInstalled()) continue;
|
|
119
|
+
|
|
120
|
+
const hasFunction = await app.isFunctionInApp(functionName)
|
|
121
|
+
if (hasFunction) {
|
|
122
|
+
return await this.#vaderApiClient.invokeFunction(app,
|
|
123
|
+
functionName,
|
|
124
|
+
functionArgs,
|
|
125
|
+
userContext);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const resp = new AppFunctionResponse()
|
|
130
|
+
resp.status = 'notfound'
|
|
131
|
+
resp.data = null
|
|
132
|
+
resp.functionName = functionName
|
|
133
|
+
resp.functionArgs = functionArgs
|
|
134
|
+
resp.message = `Function not found: ${functionName}`
|
|
135
|
+
return resp
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
async invokeHook(app, hookName, hookArgs, userContext = null) {
|
|
140
|
+
const appVersionIds = await userContext.getAppVersionIds()
|
|
141
|
+
for (const appVersionId of appVersionIds) {
|
|
142
|
+
const [appId, appVersion] = appVersionId.split(':')
|
|
143
|
+
const app = await this.getApp(appId, appVersion, null, userContext);
|
|
144
|
+
if (!app.isInstalled()) continue;
|
|
145
|
+
|
|
146
|
+
const hasHook = await app.isHookInApp(hookName)
|
|
147
|
+
if (hasHook) {
|
|
148
|
+
this.logger.info('VADER: invokeHook', hookName, 'found')
|
|
149
|
+
return await this.#vaderApiClient.invokeHook(app,
|
|
150
|
+
hookName,
|
|
151
|
+
hookArgs,
|
|
152
|
+
userContext);
|
|
153
|
+
} else {
|
|
154
|
+
this.logger.info('VADER: invokeHook', hookName, 'not found')
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return null
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
/***********************************************************************************************
|
|
163
|
+
* DATA FETCHERS
|
|
164
|
+
***********************************************************************************************/
|
|
165
|
+
async getFunctions(onlyEnabled = false, userContext) {
|
|
166
|
+
return await this.#vaderApiClient.invokeAppManager('getFunctions',
|
|
167
|
+
[onlyEnabled],
|
|
168
|
+
userContext);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async getHooks(userContext) {
|
|
173
|
+
return await this.#vaderApiClient.invokeAppManager('getHooks',
|
|
174
|
+
[],
|
|
175
|
+
userContext);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
async listApps(userContext) {
|
|
180
|
+
return await this.#vaderApiClient.invokeAppManager('listApps',
|
|
181
|
+
[],
|
|
182
|
+
userContext);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
module.exports = { VidaLiveAppManager };
|