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

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 +32 -0
  2. package/dist/{index-92deaa5f.cjs → exports-e7d51b70.cjs} +46618 -4591
  3. package/dist/index.cjs +58 -15
  4. package/dist/{multipart-parser-090f08a9.cjs → multipart-parser-e09a67c9.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 +30 -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 +131 -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 +158 -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
@@ -1,17 +1,171 @@
1
1
  import path from 'node:path';
2
2
  import { requireProjectFile } from '../../utils/index.js';
3
3
  import { globSync } from 'glob';
4
+ import { pathToFileURL } from 'node:url';
5
+ import fss from 'node:fs';
6
+ import fs from 'node:fs/promises';
7
+ import { writeFileToLocal } from '../../utils/file.js';
8
+ import { Configuration, Scout9Api } from '@scout9/admin';
9
+ import { projectTemplates } from '../../utils/project-templates.js';
10
+ import imageBuffer from '../../utils/image-buffer.js';
4
11
 
5
- export default async function loadProjectConfig({cwd = process.cwd(), folder = 'src'} = {}) {
6
- console.log('loadProjectConfig', {cwd, folder});
7
- const paths = globSync(path.resolve(cwd, `${folder}/index.{ts,js}`));
12
+
13
+ /**
14
+ * @param {Object} input
15
+ * @param {string | Buffer} input.img
16
+ * @param {'icon' | 'logo'} [input.type]
17
+ * @returns {Promise<string>}
18
+ */
19
+ async function writeImageToServer({img, type = 'icon', source}) {
20
+ const buffer = await imageBuffer(img, type === 'icon', source).then((res => res.buffer));
21
+ const scout9 = new Scout9Api(new Configuration({apiKey: process.env.SCOUT9_API_KEY}));
22
+ let url;
23
+ if (type === 'icon') {
24
+ const result = await scout9.organizationIcon(buffer).then(res => res.data);
25
+ url = result.url;
26
+ } else {
27
+ const result = await scout9.organizationLogo(buffer).then(res => res.data);
28
+ url = result.url;
29
+ }
30
+ if (!url) {
31
+ throw new Error(`Failed to upload agent image or no data returned: ${url}`);
32
+ }
33
+ return url;
34
+ }
35
+
36
+
37
+ /**
38
+ * Loads the users local package.json, if they provide a package-s9-test.json,
39
+ * it will load that instead (used for scout9 internal testing)
40
+ * @param {cwd?: string}
41
+ * @returns {Promise<{isTest: boolean, pkg: {name: string; version: string; dependencies: Record<string, string>}}>}
42
+ */
43
+ export async function loadUserPackageJson({cwd = process.cwd()}) {
44
+ const packageJsonPath = path.resolve(cwd, './package.json');
45
+ const packageTestJsonPath = path.resolve(cwd, './package-s9-test.json');
46
+ const packageJsonUrl = pathToFileURL(packageJsonPath);
47
+ const packageTestJsonUrl = pathToFileURL(packageTestJsonPath);
48
+ const isTest = fss.existsSync(packageTestJsonUrl);
49
+ const targetPkgUrl = isTest ? packageTestJsonUrl : packageJsonUrl;
50
+ const pkg = JSON.parse(await fs.readFile(new URL(targetPkgUrl, import.meta.url), 'utf-8'));
51
+ return {
52
+ pkg,
53
+ isTest
54
+ }
55
+ }
56
+
57
+ /**
58
+ * @returns {Promise<import('../../runtime/client/config.js').IScout9ProjectConfig>}
59
+ */
60
+ export default async function loadProjectConfig({cwd = process.cwd(), deploying = false, src = 'src', cb = (message) => {}} = {}) {
61
+ // Grab the project tag name (from their package.json)
62
+ const {pkg} = await loadUserPackageJson({cwd});
63
+ const tag = `${pkg.name || 'scout9-app'}-v${pkg.version || '0.0.1'}`;
64
+
65
+ // Grab the project config file (from src/index.{ts,js})
66
+ const paths = globSync(`${src}/index.{ts,cjs,mjs,js}`, {cwd, absolute: true});
8
67
  if (paths.length === 0) {
9
- throw new Error(`Missing main project entry file ${folder}/index.{js|ts}`);
68
+ throw new Error(`Missing main project entry file ${src}/index.{js|ts|cjs|mjs}`);
10
69
  } else if (paths.length > 1) {
11
- throw new Error(`Multiple main project entry files found ${folder}/index.{js|ts}`);
70
+ throw new Error(`Multiple main project entry files found ${src}/index.{js|ts|cjs|mjs}`);
12
71
  }
13
72
  const [filePath] = paths;
14
73
  const project = await requireProjectFile(filePath).then(mod => mod.default);
74
+
75
+ let serverDeployed = false;
76
+
77
+ if (project.organization.logo) {
78
+ if (typeof project.organization.logo === 'string') {
79
+ if (!project.organization.logo.startsWith('https://storage.googleapis.com')) {
80
+ if (deploying) {
81
+ project.organization.logo = await writeImageToServer({img: project.organization.logo, type: 'logo', source: filePath});
82
+ cb(`✅ Uploaded logo to server: ${project.organization.logo}`);
83
+ serverDeployed = true;
84
+ } else {
85
+ project.organization.logo = await writeFileToLocal({file: project.organization.logo, fileName: 'logo', source: filePath})
86
+ .then(({uri, isImage}) => {
87
+ if (!isImage) {
88
+ throw new Error(`Invalid image type: ${uri}`);
89
+ }
90
+ return uri;
91
+ });
92
+ cb(`✅ Wrote logo to local: ${project.organization.logo}`);
93
+ }
94
+ }
95
+ } else if (Buffer.isBuffer(project.organization.logo)) {
96
+ if (deploying) {
97
+ project.organization.logo = await writeImageToServer({img: project.organization.logo, type: 'logo', source: filePath});
98
+ cb(`✅ Uploaded logo to server: ${project.organization.logo}`);
99
+ serverDeployed = true;
100
+ } else {
101
+ project.organization.logo = await writeFileToLocal({file: project.organization.logo, fileName: 'logo', source: filePath})
102
+ .then(({uri, isImage}) => {
103
+ if (!isImage) {
104
+ throw new Error(`Invalid image type: ${uri}`);
105
+ }
106
+ return uri;
107
+ });
108
+ cb(`✅ Wrote logo to local: ${project.organization.logo}`);
109
+ }
110
+ } else {
111
+ throw new Error(`Invalid logo type: ${typeof project.organization.logo}`);
112
+ }
113
+ }
114
+
115
+ if (project.organization.icon) {
116
+ if (typeof project.organization.icon === 'string') {
117
+ if (!project.organization.icon.startsWith('https://storage.googleapis.com')) {
118
+ if (deploying) {
119
+ project.organization.icon = await writeImageToServer({img: project.organization.icon, type: 'icon', source: filePath});
120
+ cb(`✅ Uploaded icon to server: ${project.organization.icon}`);
121
+ serverDeployed = true;
122
+ } else {
123
+ project.organization.icon = await writeFileToLocal({file: project.organization.icon, fileName: 'icon', source: filePath})
124
+ .then(({uri, isImage}) => {
125
+ if (!isImage) {
126
+ throw new Error(`Invalid image type: ${uri}`);
127
+ }
128
+ return uri;
129
+ });
130
+ cb(`✅ Wrote icon to local: ${project.organization.icon}`);
131
+ }
132
+ }
133
+ } else if (Buffer.isBuffer(project.organization.icon)) {
134
+ if (deploying) {
135
+ project.organization.icon = await writeImageToServer({img: project.organization.icon, type: 'icon', source: filePath});
136
+ cb(`✅ Uploaded icon to server: ${project.organization.icon}`);
137
+ serverDeployed = true;
138
+ } else {
139
+ project.organization.icon = await writeFileToLocal({file: project.organization.icon, fileName: 'icon', source: filePath})
140
+ .then(({uri, isImage}) => {
141
+ if (!isImage) {
142
+ throw new Error(`Invalid image type: ${uri}`);
143
+ }
144
+ return uri;
145
+ });
146
+ cb(`✅ Wrote icon to local: ${project.organization.icon}`);
147
+ }
148
+ } else {
149
+ throw new Error(`Invalid icon type: ${typeof project.organization.icon}`);
150
+ }
151
+ }
152
+
15
153
  // @TODO validation type check with zod
16
- return project;
154
+
155
+
156
+ if (serverDeployed) {
157
+ cb(`Syncing ${filePath} with latest server changes`);
158
+ await fs.writeFile(filePath, projectTemplates.root({tag, ...project}, path.extname(filePath)));
159
+ // const update = await p.confirm({
160
+ // message: `Changes uploaded, sync local entities/agents file?`,
161
+ // initialValue: true
162
+ // });
163
+ // if (update) {
164
+ // }
165
+ }
166
+
167
+ return {
168
+ tag,
169
+ ...project,
170
+ };
17
171
  }
@@ -1,38 +1,39 @@
1
1
  import { globSync } from 'glob';
2
- import path from 'node:path';
3
- import {
4
- WorkflowConfigurationSchema,
5
- WorkflowsConfigurationSchema
6
- } from '../../runtime/index.js';
2
+ import { WorkflowConfigurationSchema, WorkflowsConfigurationSchema } from '../../runtime/index.js';
7
3
 
8
4
 
5
+ /**
6
+ * @returns {Promise<import('../../runtime/client/workflow.js').IWorkflowsConfiguration>}
7
+ */
9
8
  export default async function loadWorkflowsConfig(
10
9
  {
11
10
  cwd = process.cwd(),
12
- folder = 'src'
11
+ src = 'src',
12
+ cb = (message) => {}
13
13
  } = {}
14
14
  ) {
15
- console.log('loadWorkflowsConfig', {cwd, folder});
16
- const config = globSync(path.resolve(cwd, `${folder}/workflows/**/workflow.{ts,js}`))
15
+ // const config = globSync(path.resolve(cwd, `${src}/workflows/**/workflow.{ts,js}`), {cwd, absolute: true})
16
+ const config = globSync(`${src}/workflows/**/workflow.{ts,js}`, {cwd, absolute: true})
17
17
  .map((path) => {
18
18
  const segments = path.split('/');
19
- const srcIndex = segments.findIndex((segment, index) => segment === folder && segments[index + 1] === 'workflows');
20
- const parents = segments.slice(srcIndex + 2, -1).reverse(); // +2 to skip "${folder}" and "workflows"
19
+ const srcIndex = segments.findIndex((segment, index) => segment === src && segments[index + 1] === 'workflows');
20
+ const parents = segments.slice(srcIndex + 2, -1).reverse(); // +2 to skip "${src}" and "workflows"
21
21
  return {path, parents};
22
22
  })
23
23
  .filter(path => {
24
24
  if (path.parents.length > 0) {
25
25
  return true;
26
26
  } else {
27
- console.log(`WARNING: "${path}" Is not a valid entity path, must be contained in a named folder under workflows/`);
27
+ console.log(`WARNING: "${path}" Is not a valid entity path, must be contained in a named src under workflows/`);
28
28
  }
29
29
  })
30
30
  .map(({path, parents}) => {
31
31
 
32
32
  // Validate project configuration
33
+ /** @type {import('../../runtime/client/workflow.js').IWorkflowConfiguration} */
33
34
  const workflowConfig = {
34
35
  entity: parents[0],
35
- entities: parents,
36
+ entities: parents.reverse(),
36
37
  }
37
38
  WorkflowConfigurationSchema.parse(workflowConfig);
38
39
 
@@ -0,0 +1,27 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ export async function platformApi(url, options = {}, retries = 0) {
4
+ if (retries > 3) {
5
+ throw new Error(`Request timed out, try again later`);
6
+ }
7
+ if (!process.env.SCOUT9_API_KEY) {
8
+ throw new Error('Missing required environment variable "SCOUT9_API_KEY"');
9
+ }
10
+ return fetch(url, {
11
+ method: 'GET',
12
+ ...options,
13
+ headers: {
14
+ 'Authorization': process.env.SCOUT9_API_KEY || '',
15
+ ...(options.headers || {})
16
+ }
17
+ }).then((res) => {
18
+ if (res.status === 504) {
19
+ return new Promise((resolve, reject) => {
20
+ setTimeout(() => {
21
+ resolve(platformApi(url, options, retries + 1));
22
+ }, 3000);
23
+ });
24
+ }
25
+ return Promise.resolve(res);
26
+ });
27
+ }