@scout9/app 1.0.0-alpha.0.1.9 → 1.0.0-alpha.0.1.91

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 (70) hide show
  1. package/README.md +33 -0
  2. package/dist/{index-92deaa5f.cjs → exports-212ef6be.cjs} +46636 -4591
  3. package/dist/index.cjs +58 -15
  4. package/dist/{multipart-parser-090f08a9.cjs → multipart-parser-54a3ab5f.cjs} +13 -7
  5. package/dist/spirits-3b603262.cjs +1218 -0
  6. package/dist/spirits.cjs +9 -0
  7. package/dist/testing-tools.cjs +48 -0
  8. package/package.json +37 -8
  9. package/src/cli.js +162 -69
  10. package/src/core/config/agents.js +300 -7
  11. package/src/core/config/entities.js +58 -28
  12. package/src/core/config/index.js +37 -15
  13. package/src/core/config/project.js +160 -6
  14. package/src/core/config/workflow.js +13 -12
  15. package/src/core/data.js +27 -0
  16. package/src/core/index.js +386 -137
  17. package/src/core/sync.js +71 -0
  18. package/src/core/templates/Dockerfile +22 -0
  19. package/src/core/templates/app.js +453 -0
  20. package/src/core/templates/project-files.js +36 -0
  21. package/src/core/templates/template-package.json +13 -0
  22. package/src/exports.js +21 -17
  23. package/src/platform.js +189 -33
  24. package/src/public.d.ts.text +330 -0
  25. package/src/report.js +117 -0
  26. package/src/runtime/client/api.js +56 -159
  27. package/src/runtime/client/config.js +60 -11
  28. package/src/runtime/client/entity.js +19 -6
  29. package/src/runtime/client/index.js +5 -3
  30. package/src/runtime/client/message.js +13 -3
  31. package/src/runtime/client/platform.js +86 -0
  32. package/src/runtime/client/{agent.js → users.js} +35 -3
  33. package/src/runtime/client/utils.js +10 -9
  34. package/src/runtime/client/workflow.js +132 -9
  35. package/src/runtime/entry.js +2 -2
  36. package/src/testing-tools/dev.js +373 -0
  37. package/src/testing-tools/index.js +1 -0
  38. package/src/testing-tools/mocks.js +37 -5
  39. package/src/testing-tools/spirits.js +530 -0
  40. package/src/utils/audio-buffer.js +16 -0
  41. package/src/utils/audio-type.js +27 -0
  42. package/src/utils/configs/agents.js +68 -0
  43. package/src/utils/configs/entities.js +145 -0
  44. package/src/utils/configs/project.js +23 -0
  45. package/src/utils/configs/workflow.js +47 -0
  46. package/src/utils/file-type.js +569 -0
  47. package/src/utils/file.js +164 -0
  48. package/src/utils/glob.js +30 -0
  49. package/src/utils/image-buffer.js +23 -0
  50. package/src/utils/image-type.js +39 -0
  51. package/src/utils/index.js +1 -0
  52. package/src/utils/is-svg.js +37 -0
  53. package/src/utils/logger.js +111 -0
  54. package/src/utils/module.js +14 -25
  55. package/src/utils/project-templates.js +191 -0
  56. package/src/utils/project.js +387 -0
  57. package/src/utils/video-type.js +29 -0
  58. package/types/index.d.ts +7588 -206
  59. package/types/index.d.ts.map +97 -22
  60. package/dist/index-1b8d7dd2.cjs +0 -49555
  61. package/dist/index-2ccb115e.cjs +0 -49514
  62. package/dist/index-66b06a30.cjs +0 -49549
  63. package/dist/index-bc029a1d.cjs +0 -49528
  64. package/dist/index-d9a93523.cjs +0 -49527
  65. package/dist/multipart-parser-1508046a.cjs +0 -413
  66. package/dist/multipart-parser-7007403a.cjs +0 -413
  67. package/dist/multipart-parser-70c32c1d.cjs +0 -413
  68. package/dist/multipart-parser-71dec101.cjs +0 -413
  69. package/dist/multipart-parser-f15bf2e0.cjs +0 -414
  70. package/src/public.d.ts +0 -209
@@ -0,0 +1,71 @@
1
+ import { platformApi } from './data.js';
2
+
3
+ /**
4
+ * @param {import('../runtime/client/config.js').IScout9ProjectBuildConfig} config
5
+ * @returns {Promise<import('../runtime/client/config.js').IScout9ProjectBuildConfig>}
6
+ */
7
+ export async function syncData(config) {
8
+ if (!process.env.SCOUT9_API_KEY) {
9
+ throw new Error('Missing required environment variable "SCOUT9_API_KEY"');
10
+ }
11
+ const result = await platformApi(`https://scout9.com/api/b/platform/sync`).then((res) => {
12
+ if (res.status !== 200) {
13
+ throw new Error(`Server responded with ${res.status}: ${res.statusText}`);
14
+ }
15
+ return res.json();
16
+ })
17
+ .catch((err) => {
18
+ err.message = `Error fetching entities and agents: ${err.message}`;
19
+ throw err;
20
+ });
21
+
22
+ const { agents, entities, organization, initialContext } = result;
23
+
24
+ // Merge
25
+ config.agents = agents.reduce((accumulator, agent) => {
26
+ // Check if agent already exists
27
+ const existingAgentIndex = accumulator.findIndex(a => a.id === agent.id);
28
+ if (existingAgentIndex === -1) {
29
+ accumulator.push(agent);
30
+ } else {
31
+ // Merge agent
32
+ accumulator[existingAgentIndex] = {
33
+ ...accumulator[existingAgentIndex],
34
+ ...agent
35
+ };
36
+ }
37
+ return accumulator;
38
+ }, config.agents);
39
+
40
+ // Remove agents that are not on the server
41
+ config.agents = config.agents.filter(agent => agents.find(a => a.id === agent.id));
42
+
43
+ // Merge entities
44
+ config.entities = entities.reduce((accumulator, entity) => {
45
+ // Check if agent already exists
46
+ const existingEntityIndex = accumulator.findIndex(a => a.id === entity.id);
47
+ if (existingEntityIndex === -1) {
48
+ accumulator.push(entity);
49
+ } else {
50
+ // Merge agent
51
+ accumulator[existingEntityIndex] = {
52
+ ...accumulator[existingEntityIndex],
53
+ ...entity
54
+ };
55
+ }
56
+ return accumulator;
57
+ }, config.entities);
58
+
59
+ // Remove entities that are not on the server
60
+ config.entities = config.entities.filter(entity => entities.find(a => a.id === entity.id));
61
+ config.organization = {
62
+ ...(config?.organization || {}),
63
+ ...(organization || {})
64
+ };
65
+ config.initialContext = [
66
+ ...(Array.isArray(config?.initialContext) ? config.initialContext : []),
67
+ ...(Array.isArray(initialContext) ? initialContext : [])
68
+ ];
69
+
70
+ return config;
71
+ }
@@ -0,0 +1,22 @@
1
+ # Use Node.js version 16
2
+ FROM node:18-slim
3
+
4
+ # Create and change to the app directory.
5
+ WORKDIR /usr/src/app
6
+
7
+ # Copy application dependency manifests to the container image.
8
+ # A wildcard is used to ensure both package.json AND package-lock.json are copied.
9
+ # Copying this separately prevents re-running npm install on every code change.
10
+ COPY package*.json ./
11
+
12
+ # Install production dependencies.
13
+ RUN npm install --only=production
14
+
15
+ # Copy local code to the container image.
16
+ COPY . ./
17
+
18
+ # Expose port for Cloud Run
19
+ EXPOSE 8080
20
+
21
+ # Run the web service on container startup.
22
+ CMD [ "npm", "start" ]
@@ -0,0 +1,453 @@
1
+ import polka from 'polka';
2
+ import sirv from 'sirv';
3
+ import compression from 'compression';
4
+ import bodyParser from 'body-parser';
5
+ import colors from 'kleur';
6
+ import { config as dotenv } from 'dotenv';
7
+ import { Configuration, Scout9Api } from '@scout9/admin';
8
+ import { EventResponse } from '@scout9/app';
9
+ import path from 'node:path';
10
+ import fs from 'node:fs';
11
+ import https from 'node:https';
12
+ import { fileURLToPath, pathToFileURL } from 'node:url';
13
+ import projectApp from './src/app.js';
14
+ import config from './config.js';
15
+
16
+ const __filename = fileURLToPath(import.meta.url);
17
+ const __dirname = path.dirname(__filename);
18
+
19
+ const dev = process.env.DEV_MODE === 'true' || process.env.TEST_MODE === 'true';
20
+
21
+ class ServerCache {
22
+ constructor(filePath = path.resolve(__dirname, './server.cache.json')) {
23
+ this.filePath = filePath;
24
+ this._load();
25
+ }
26
+
27
+ isTested() {
28
+ return this._cache.tested === true;
29
+ }
30
+
31
+ setTested(value = true) {
32
+ this._cache.tested = value;
33
+ this._save();
34
+ }
35
+
36
+ reset(override = {tested: false}) {
37
+ this._save(override);
38
+ this._cache = override;
39
+ }
40
+
41
+ _load() {
42
+ try {
43
+ this._cache = JSON.parse(fs.readFileSync(this.filePath, 'utf8'));
44
+ return this._cache;
45
+ } catch (e) {
46
+ if (e.code === 'ENOENT') {
47
+ this._save();
48
+ } else {
49
+ throw e;
50
+ }
51
+ }
52
+ }
53
+
54
+ _save(override) {
55
+ fs.writeFileSync(this.filePath, JSON.stringify(override || this._cache || {tested: false}));
56
+ }
57
+ }
58
+
59
+
60
+ // Ensure .env config is set (specifically SCOUT9_API_KEY)
61
+ const configFilePath = path.resolve(process.cwd(), './.env');
62
+ dotenv({path: configFilePath});
63
+
64
+ const configuration = new Configuration({
65
+ apiKey: process.env.SCOUT9_API_KEY || ''
66
+ });
67
+ const scout9 = new Scout9Api(configuration);
68
+ const cache = new ServerCache();
69
+ cache.reset();
70
+
71
+
72
+ const handleError = (e, res = undefined) => {
73
+ let name = e?.name || 'Runtime Error';
74
+ let message = e?.message || 'Unknown error';
75
+ let code = typeof e?.code === 'number'
76
+ ? e?.code
77
+ : typeof e?.status === 'number'
78
+ ? e?.status
79
+ : 500;
80
+ if ('response' in e) {
81
+ const response = e.response;
82
+ if (response?.status) {
83
+ code = response.status;
84
+ }
85
+ if (response?.statusText) {
86
+ name = response.statusText;
87
+ }
88
+ if (response?.data) {
89
+ message = response.data;
90
+ } else if (response?.body) {
91
+ message = response.body;
92
+ }
93
+ }
94
+ console.log(colors.red(`${colors.bold(`${code} Error`)}: ${message}`));
95
+ if (res) {
96
+ res.writeHead(code, {'Content-Type': 'application/json'});
97
+ res.end(JSON.stringify({
98
+ name,
99
+ error: message,
100
+ code
101
+ }));
102
+ }
103
+ };
104
+
105
+ const makeRequest = async (options, maxRedirects = 10) => {
106
+ return new Promise((resolve, reject) => {
107
+
108
+ if (maxRedirects < 0) {
109
+ reject(new Error('Too many redirects'));
110
+ return;
111
+ }
112
+
113
+ const req = https.request(options, (res) => {
114
+ console.log(`STATUS: ${res.statusCode}`);
115
+ if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
116
+ // Handle redirect
117
+ console.log(`Redirecting to ${res.headers.location}`);
118
+ const newUrl = new URL(res.headers.location);
119
+ const newOptions = {
120
+ hostname: newUrl.hostname,
121
+ port: newUrl.port || 443,
122
+ path: newUrl.pathname,
123
+ method: 'GET', // Usually redirects are GET, adjust if necessary
124
+ headers: options.headers // Reuse original headers
125
+ // Add any other necessary options here
126
+ };
127
+ // Recursive call to handle redirect
128
+ resolve(makeRequest(newOptions, maxRedirects - 1));
129
+ } else {
130
+ let data = '';
131
+ res.setEncoding('utf8');
132
+ res.on('data', (chunk) => data += chunk);
133
+ res.on('end', () => {
134
+ try {
135
+ resolve(JSON.parse(data));
136
+ } catch (e) {
137
+ reject(e);
138
+ }
139
+ });
140
+ }
141
+ });
142
+
143
+ req.on('error', (e) => {
144
+ reject(e);
145
+ });
146
+
147
+ req.end();
148
+
149
+ });
150
+ };
151
+
152
+
153
+ const app = polka();
154
+
155
+ app.use(bodyParser.json());
156
+
157
+ if (dev) {
158
+ app.use(compression());
159
+ app.use(sirv(path.resolve(__dirname, 'public'), {dev: true}));
160
+ }
161
+
162
+ // Root application POST endpoint will run the scout9 app
163
+ app.post(dev ? '/dev/workflow' : '/', async (req, res) => {
164
+ try {
165
+ // @TODO use zod to check if req.body is a valid event object
166
+ const response = await projectApp(req.body);
167
+ res.writeHead(200, {'Content-Type': 'application/json'});
168
+ res.end(JSON.stringify(response));
169
+ } catch (e) {
170
+ handleError(e, res);
171
+ }
172
+ });
173
+
174
+ function isSurroundedByBrackets(str) {
175
+ return /^\[.*\]$/.test(str);
176
+ }
177
+
178
+ function resolveEntity(entity, method) {
179
+ const entityField = config.entities.find(e => e.entity === entity);
180
+ if (!entityField) {
181
+ console.error(`Invalid entity: "${entity}" not found within [${config.entities.map(e => e.entity).join(', ')}]`);
182
+ throw new Error(`Invalid entity: not found`);
183
+ }
184
+ const {api, entities} = entityField;
185
+ if (!api && !api[method]) {
186
+ throw new Error(`Invalid entity: no API`);
187
+ }
188
+ if (!entities) {
189
+ throw new Error(`Invalid entity: no path`);
190
+ }
191
+ return entityField;
192
+ }
193
+
194
+ async function resolveEntityApi(entity, method) {
195
+ const paramEntity = isSurroundedByBrackets(entity);
196
+ if (method === 'GET' && !paramEntity) {
197
+ method = 'QUERY';
198
+ }
199
+ const methods = ['GET', 'UPDATE', 'QUERY', 'PUT', 'PATCH', 'DELETE'];
200
+ if (!methods.includes(method)) {
201
+ throw new Error(`Invalid method: ${method}`);
202
+ }
203
+ const {api, entities} = resolveEntity(entity, method);
204
+ const mod = await import(pathToFileURL(path.resolve(__dirname, `./src/entities/${entities.join('/')}/api.js`)).href)
205
+ .catch((e) => {
206
+ switch (e.code) {
207
+ case 'ERR_MODULE_NOT_FOUND':
208
+ case 'MODULE_NOT_FOUND':
209
+ console.error(e);
210
+ throw new Error(`Invalid entity: no API method`);
211
+ default:
212
+ console.error(e);
213
+ throw new Error(`Invalid entity: Internal system error`);
214
+ }
215
+ });
216
+ if (mod[method]) {
217
+ return mod[method];
218
+ }
219
+
220
+ if (method === 'QUERY' && mod['GET']) {
221
+ return mod['GET'];
222
+ }
223
+
224
+ throw new Error(`Invalid entity: no API method`);
225
+
226
+ }
227
+
228
+ function extractParamsFromPath(path) {
229
+ const segments = path.split('/').filter(Boolean); // Split and remove empty segments
230
+ let params = {};
231
+ const dataStructure = config.entities;
232
+
233
+ // Assuming the structure starts with "/entity/"
234
+ segments.shift(); // remove the "entity" segment
235
+
236
+ let lastEntity;
237
+ let lastSegment;
238
+ segments.forEach(segment => {
239
+ const isEntity = dataStructure.some(d => d.entity === segment || d.entity === `[${segment}]`);
240
+ if (isEntity) {
241
+ lastEntity = segment;
242
+ lastSegment = segment;
243
+ } else if (lastEntity) {
244
+ const entityDefinition = dataStructure.find(d => {
245
+ const index = d.entities.indexOf(lastEntity);
246
+ return index > -1 && index === (d.entities.length - 2);
247
+ });
248
+ if (entityDefinition) {
249
+ const paramName = entityDefinition.entity.replace(/[\[\]]/g, ''); // Remove brackets to get the param name
250
+ params[paramName] = segment;
251
+ lastSegment = entityDefinition.entity;
252
+ }
253
+ lastEntity = null; // Reset for next potential entity-instance pair
254
+ }
255
+ });
256
+
257
+ return {params, lastEntity, lastSegment};
258
+ }
259
+
260
+ async function runEntityApi(req, res) {
261
+ try {
262
+ // polka doesn't support wildcards
263
+ const {params, lastSegment} = extractParamsFromPath(req.url);
264
+ const api = await resolveEntityApi(lastSegment, req.method.toUpperCase());
265
+ const response = await api({
266
+ params,
267
+ searchParams: req?.query || {}, body: req?.body || undefined,
268
+ id: params.id
269
+ });
270
+ if (response instanceof EventResponse && !!response.body) {
271
+ res.writeHead(response.status || 200, {'Content-Type': 'application/json'});
272
+ res.end(JSON.stringify(response.body));
273
+ } else {
274
+ throw new Error(`Invalid response: not an EventResponse`);
275
+ }
276
+ } catch (e) {
277
+ console.error(e);
278
+ res.writeHead(500, {'Content-Type': 'application/json'});
279
+ res.end(JSON.stringify({error: e.message}));
280
+ }
281
+ }
282
+
283
+ app.get('/entity/:entity', runEntityApi);
284
+ app.put('/entity/:entity', runEntityApi);
285
+ app.patch('/entity/:entity', runEntityApi);
286
+ app.post('/entity/:entity', runEntityApi);
287
+ app.delete('/entity/:entity', runEntityApi);
288
+ app.get('/entity/:entity/*', runEntityApi);
289
+ app.put('/entity/:entity/*', runEntityApi);
290
+ app.patch('/entity/:entity/*', runEntityApi);
291
+ app.post('/entity/:entity/*', runEntityApi);
292
+ app.delete('/entity/:entity/*', runEntityApi);
293
+
294
+ // For local development: parse a message
295
+ if (dev) {
296
+
297
+ app.get('/dev/config', async (req, res, next) => {
298
+
299
+ // Retrieve auth token
300
+ const {token, id} = await makeRequest({
301
+ hostname: 'us-central1-jumpstart.cloudfunctions.net',
302
+ port: 443,
303
+ path: '/v1-utils-platform-token',
304
+ method: 'GET',
305
+ headers: {
306
+ 'Authorization': 'Bearer ' + process.env.SCOUT9_API_KEY
307
+ }
308
+ });
309
+ res.writeHead(200, {'Content-Type': 'application/json'});
310
+ res.end(JSON.stringify({token, id, ...config}));
311
+ try {
312
+ if (!cache.isTested()) {
313
+ const testableEntities = config.entities.filter(e => e?.definitions?.length > 0 || e?.training?.length > 0);
314
+ if (dev && testableEntities.length > 0) {
315
+ console.log(`${colors.grey(`${colors.cyan('>')} Testing ${colors.bold(colors.white(testableEntities.length))} Entities...`)}`);
316
+ const _res = await scout9.parse({
317
+ message: 'Dummy message to parse',
318
+ language: 'en',
319
+ entities: testableEntities
320
+ });
321
+ cache.setTested();
322
+ console.log(`\t${colors.green(`+ ${testableEntities.length} Entities passed`)}`);
323
+ }
324
+ }
325
+ } catch (e) {
326
+ console.error(e);
327
+ handleError(e);
328
+ }
329
+ });
330
+
331
+ app.post('/dev/parse', async (req, res, next) => {
332
+ try {
333
+ // req.body: {message: string}
334
+ const {message, language} = req.body;
335
+ if (typeof message !== 'string') {
336
+ throw new Error('Invalid message - expected to be a string');
337
+ }
338
+ console.log(`${colors.grey(`${colors.cyan('>')} Parsing "${colors.bold(colors.white(message))}`)}"`);
339
+ const payload = await scout9.parse({
340
+ message,
341
+ language: 'en',
342
+ entities: config.entities
343
+ }).then((_res => _res.data));
344
+ let fields = '';
345
+ for (const [key, value] of Object.entries(payload.context)) {
346
+ fields += `\n\t\t${colors.bold(colors.white(key))}: ${colors.grey(JSON.stringify(value))}`;
347
+ }
348
+ console.log(`\tParsed in ${payload.ms}ms:${colors.grey(`${fields}`)}`);
349
+ res.writeHead(200, {'Content-Type': 'application/json'});
350
+ res.end(JSON.stringify(payload));
351
+ } catch (e) {
352
+ handleError(e, res);
353
+ }
354
+ });
355
+
356
+ app.post('/dev/forward', async (req, res, next) => {
357
+ try {
358
+ // req.body: {message: string}
359
+ const {convo} = req.body;
360
+ console.log(`${colors.grey(`${colors.cyan('>')} Forwarding...`)}`);
361
+ const payload = await scout9.forward({convo}).then((_res => _res.data));
362
+ console.log(`\tForwarded in ${payload?.ms}ms`);
363
+ res.writeHead(200, {'Content-Type': 'application/json'});
364
+ res.end(JSON.stringify(payload));
365
+ } catch (e) {
366
+ handleError(e, res);
367
+ }
368
+ });
369
+
370
+ app.post('/dev/generate', async (req, res, next) => {
371
+ try {
372
+ // req.body: {conversation: {}, messages: []}
373
+ const {messages, persona: personaId} = req.body;
374
+ if (typeof messages !== 'object' || !Array.isArray(messages)) {
375
+ throw new Error('Invalid messages array - expected to be an array of objects');
376
+ }
377
+ if (typeof personaId !== 'string') {
378
+ throw new Error('Invalid persona - expected to be a string');
379
+ }
380
+ const persona = (config.persona || config.agents).find(p => p.id === personaId);
381
+ if (!persona) {
382
+ throw new Error(`Could not find persona with id: ${personaId}, ensure your project is sync'd by running "scout9 sync"`);
383
+ }
384
+ console.log(`${colors.grey(`${colors.cyan('>')} Generating ${colors.bold(colors.white(persona.firstName))}'s`)} ${colors.bold(
385
+ colors.red(colors.bgBlack('auto-reply')))}`);
386
+ const payload = await scout9.generate({
387
+ messages,
388
+ persona,
389
+ llm: config.llm,
390
+ pmt: config.pmt
391
+ }).then((_res => _res.data));
392
+ console.log(`\t${colors.grey(`Response: ${colors.green('"')}${colors.bold(colors.white(payload.message))}`)}${colors.green(
393
+ '"')} (elapsed ${payload.ms}ms)`);
394
+ res.writeHead(200, {'Content-Type': 'application/json'});
395
+ res.end(JSON.stringify(payload));
396
+ } catch (e) {
397
+ handleError(e, res);
398
+ }
399
+ });
400
+ }
401
+
402
+
403
+ app.listen(process.env.PORT || 8080, err => {
404
+ if (err) throw err;
405
+
406
+ const art_scout9 = `
407
+
408
+
409
+ ________ ________ ________ ___ ___ _________ ________
410
+ |\\ ____\\|\\ ____\\|\\ __ \\|\\ \\|\\ \\|\\___ ___\\\\ ___ \\
411
+ \\ \\ \\___|\\ \\ \\___|\\ \\ \\|\\ \\ \\ \\\\\\ \\|___ \\ \\_\\ \\____ \\
412
+ \\ \\_____ \\ \\ \\ \\ \\ \\\\\\ \\ \\ \\\\\\ \\ \\ \\ \\ \\|____|\\ \\
413
+ \\|____|\\ \\ \\ \\____\\ \\ \\\\\\ \\ \\ \\\\\\ \\ \\ \\ \\ __\\_\\ \\
414
+ ____\\_\\ \\ \\_______\\ \\_______\\ \\_______\\ \\ \\__\\ |\\_______\\
415
+ |\\_________\\|_______|\\|_______|\\|_______| \\|__| \\|_______|
416
+ \\|_________|
417
+ `;
418
+ const art_auto_reply = ` ___ __ ____ __ __
419
+ / | __ __/ /_____ / __ \\___ ____ / /_ __ / /
420
+ / /| |/ / / / __/ __ \\ / /_/ / _ \\/ __ \\/ / / / / / /
421
+ / ___ / /_/ / /_/ /_/ / / _, _/ __/ /_/ / / /_/ / /_/
422
+ /_/ |_\\__,_/\\__/\\____/ /_/ |_|\\___/ .___/_/\\__, / (_)
423
+ /_/ /____/
424
+
425
+ `;
426
+ const protocol = process.env.PROTOCOL || 'http';
427
+ const host = process.env.HOST || 'localhost';
428
+ const port = process.env.PORT || 8080;
429
+ const fullUrl = `${protocol}://${host}:${port}`;
430
+ if (dev) {
431
+ console.log(colors.bold(colors.green(art_scout9)));
432
+ console.log(colors.bold(colors.cyan(art_auto_reply)));
433
+ console.log(`${colors.grey(`${colors.cyan('>')} Running ${colors.bold(colors.white('Scout9'))}`)} ${colors.bold(
434
+ colors.red(colors.bgBlack('auto-reply')))} ${colors.grey('dev environment on')} ${fullUrl}`);
435
+ } else {
436
+ console.log(`Running Scout9 auto-reply app on ${fullUrl}`);
437
+ }
438
+ // Run checks
439
+ if (!fs.existsSync(configFilePath)) {
440
+ console.log(colors.red('Missing .env file, your auto reply application may not work without it.'));
441
+ }
442
+
443
+ if (dev && !process.env.SCOUT9_API_KEY) {
444
+ console.log(colors.red(
445
+ 'Missing SCOUT9_API_KEY environment variable, your auto reply application may not work without it.'));
446
+ }
447
+
448
+ if (process.env.SCOUT9_API_KEY === '<insert-scout9-api-key>') {
449
+ console.log(`${colors.red('SCOUT9_API_KEY has not been set in your .env file.')} ${colors.grey(
450
+ 'You can find your API key in the Scout9 dashboard.')} ${colors.bold(colors.cyan('https://scout9.com'))}`);
451
+ }
452
+
453
+ });
@@ -0,0 +1,36 @@
1
+ import fs from 'node:fs/promises';
2
+ import { globSync } from 'glob';
3
+
4
+
5
+ const resolveFilePath = () => {
6
+ const paths = globSync(`${src}/entities/agents/{index,config}.{ts,js}`, {cwd, absolute: true});
7
+ if (paths.length === 0) {
8
+ throw new Error(`Missing required agents entity file, rerun "scout9 sync" to fix`);
9
+ }
10
+ if (paths.length > 1) {
11
+ throw new Error(`Multiple agents entity files found, rerun "scout9 sync" to fix`);
12
+ }
13
+ }
14
+
15
+ export const entities = {
16
+ /**
17
+ * Generates ./src/entities/
18
+ * @param {import('../../runtime/client/config.js').IScout9ProjectBuildConfig} config
19
+ * @param {string} cwd
20
+ * @param {string} src
21
+ * @returns {Promise<void>}
22
+ */
23
+
24
+ agents: async function (config, cwd = process.cwd(), src = './src') {
25
+ const content = `
26
+ /**
27
+ * Required core entity type: Agents represents you and your team
28
+ * @returns {Array<Agent>}
29
+ */
30
+ export default function Agents() {
31
+ return ${JSON.stringify(config.agents, null, 2)};
32
+ }
33
+ `;
34
+ await fs.writeFile(filePath, );
35
+ }
36
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "templates",
3
+ "version": "1.0.0",
4
+ "description": "Used to ensure template app.js gets the correct dependencies.",
5
+ "dependencies": {
6
+ "body-parser": "latest",
7
+ "compression": "latest",
8
+ "dotenv": "latest",
9
+ "kleur": "latest",
10
+ "polka": "latest",
11
+ "sirv": "latest"
12
+ }
13
+ }
package/src/exports.js CHANGED
@@ -2,35 +2,39 @@ import { Scout9Platform } from './platform.js';
2
2
  import { EventResponse } from './runtime/index.js';
3
3
 
4
4
  export { EventResponse } from './runtime/index.js';
5
- // export * from './types';
5
+
6
6
  export * from './testing-tools/index.js';
7
+ export * from './runtime/client/index.js';
7
8
 
8
9
  /**
9
- * @param event
10
- * @param cwd
11
- * @param folder
12
- * @returns {Promise<*|undefined>}
10
+ * @param {import('./runtime/client/workflow.js').IWorkflowEvent} event - every workflow receives an event object
11
+ * @param {Object} options
12
+ * @param {string} [options.cwd=process.cwd()] - the working directory
13
+ * @param {string} [options.mode='production'] - the build mode
14
+ * @param {string} [options.src='./src'] - the source directory
15
+ * @param {string} options.eventSource - the source of the workflow event
16
+ * @returns {Promise<import('./runtime/client/workflow.js').IWorkflowResponse>}
13
17
  */
14
- export async function run(
15
- event,
16
- {cwd = process.cwd(), folder} = {},
17
- ) {
18
- return Scout9Platform.run(event, {cwd, folder})
18
+ export async function run(event, options) {
19
+ return Scout9Platform.run(event, options)
19
20
  }
20
21
 
21
- export async function sendEvent(
22
- event,
23
- {cwd = process.cwd(), folder} = {},
24
- ) {
25
- return Scout9Platform.run(event, {cwd, folder})
26
- }
22
+ /**
23
+ * @param {import('./runtime/client/workflow.js').IWorkflowEvent} event - every workflow receives an event object
24
+ * @param {{cwd: string; mode: 'development' | 'production'; src: string}} options - build options
25
+ * @returns {Promise<import('./runtime/client/workflow.js').IWorkflowResponse>}
26
+ */
27
+ export const sendEvent = run;
27
28
 
28
29
  /**
29
30
  * @param data {T}
30
31
  * @param init {ResponseInit | undefined}
31
- * @returns {EventResponse<T>}
32
+ * @returns {import('./runtime/client/api.js').EventResponse<T>}
32
33
  */
33
34
  export function json(data, init) {
35
+ if (data instanceof Promise) {
36
+ throw new Error(`json() does not expect a Promise. Use json(await promise) instead`);
37
+ }
34
38
  // TODO deprecate this in favour of `Response.json` when it's
35
39
  // more widely supported
36
40
  const body = JSON.stringify(data);