@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,68 @@
1
+ import path from 'node:path';
2
+ import colors from 'kleur';
3
+ import { globSync } from 'glob';
4
+ import { checkVariableType, requireProjectFile } from '../../utils/index.js';
5
+ import { agentsConfigurationSchema, agentsBaseConfigurationSchema } from '../../runtime/index.js';
6
+
7
+ /**
8
+ * @param {Array<Agent>} agents
9
+ * @returns {*}
10
+ */
11
+ export function validateAgentConfig(agents) {
12
+ // Send warnings if not properly registered
13
+ for (const agent of agents) {
14
+ if (!agent.forwardPhone && !agent.forwardEmail) {
15
+ throw new Error(`src/entities/agents.js|ts: must provide either a ".forwardPhone" or ".forwardEmail" to ${agent.firstName || JSON.stringify(agent)}.`)
16
+ }
17
+ if (!agent.programmablePhoneNumber) {
18
+ const userName = agent.firstName ? `${agent.firstName}${agent.lastName ? ' ' + agent.lastName : ''}` : agent.forwardPhone;
19
+ cb(`⚠️${colors.yellow('Warning')}: ${userName} does not have a masked phone number to do auto replies. You can register one at ${colors.cyan('https://scout9.com/b')} under ${colors.green('users')} > ${colors.green(userName)}. Then run ${colors.cyan('scout9 sync')} to update.`);
20
+ }
21
+ if (agent.forwardPhone && agents.filter(a => a.forwardPhone && (a.forwardPhone === agent.forwardPhone)).length > 1) {
22
+ throw new Error(`src/entities/agents.js|ts: ".forwardPhone: ${agent.forwardPhone}" can only be associated to one agent within your project`);
23
+ }
24
+ if (agent.forwardEmail && agents.filter(a => a.forwardEmail && (a.forwardEmail === agent.forwardEmail)).length > 1) {
25
+ throw new Error(`src/entities/agents.js|ts: ".forwardEmail: ${agent.forwardEmail}" can only be associated to one agent within your project`);
26
+ }
27
+ }
28
+
29
+ const result = agentsBaseConfigurationSchema.safeParse(agents);
30
+ if (!result.success) {
31
+ result.error.source = `src/entities/agents.js|ts`;
32
+ throw result.error;
33
+ }
34
+ return agents;
35
+ }
36
+
37
+ export default async function loadAgentConfig({cwd = process.cwd(), src = 'src', cb = (message) => {}} = {}) {
38
+ const paths = globSync(`${src}/entities/agents/{index,config}.{ts,js}`, {cwd, absolute: true});
39
+ if (paths.length === 0) {
40
+ throw new Error(`Missing required agents entity file, rerun "scout9 sync" to fix`);
41
+ }
42
+ if (paths.length > 1) {
43
+ throw new Error(`Multiple agents entity files found, rerun "scout9 sync" to fix`);
44
+ }
45
+
46
+ /**
47
+ * @type {Array<Agent> | function(): Array<Agent> | function(): Promise<Array<Agent>>}
48
+ */
49
+ const mod = await requireProjectFile(paths[0]).then(mod => mod.default);
50
+
51
+ // @type {Array<Agent>}
52
+ let agents = [];
53
+ const entityType = checkVariableType(mod);
54
+ switch (entityType) {
55
+ case 'async function':
56
+ case 'function':
57
+ agents = await mod();
58
+ break;
59
+ case 'array':
60
+ agents = mod;
61
+ break;
62
+ default:
63
+ throw new Error(`Invalid entity type (${entityType}) returned at "${path}"`);
64
+ }
65
+
66
+ return validateAgentConfig(agents);
67
+ }
68
+
@@ -0,0 +1,145 @@
1
+ import { globSync } from 'glob';
2
+ import path from 'node:path';
3
+ import {
4
+ entitiesRootProjectConfigurationSchema,
5
+ entityApiConfigurationSchema,
6
+ entityConfigurationSchema,
7
+ entityRootProjectConfigurationSchema
8
+ } from '../../runtime/index.js';
9
+ import { checkVariableType, requireOptionalProjectFile, requireProjectFile } from '../module.js';
10
+
11
+ async function loadEntityApiConfig(cwd, filePath) {
12
+ const dir = path.dirname(filePath);
13
+ const extension = path.extname(filePath);
14
+ const apiFilePath = path.resolve(dir, `api${extension}`);
15
+ const mod = await requireOptionalProjectFile(apiFilePath);
16
+
17
+ if (mod) {
18
+ const config = {};
19
+ const methods = ['GET', 'UPDATE', 'QUERY', 'PUT', 'PATCH', 'DELETE'];
20
+ for (const key in mod) {
21
+ if (methods.includes(key)) {
22
+ config[key] = true;
23
+ }
24
+ }
25
+ entityApiConfigurationSchema.parse(config);
26
+ return config;
27
+ } else {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @returns {Promise<EntitiesBuildConfig>}
34
+ */
35
+ export default async function loadEntitiesConfig(
36
+ {
37
+ cwd = process.cwd(),
38
+ src = 'src',
39
+ // logger,
40
+ // cb = (message) => {}
41
+ } = {}
42
+ ) {
43
+ /** @type EntitiesBuildConfig */
44
+ const config = [];
45
+ // const paths = globSync(path.resolve(cwd, `${src}/entities/**/{index,config,api}.{ts,js}`), {cwd, absolute: true});
46
+ const filePaths = globSync(`${src}/entities/**/{index,config,api}.{ts,js}`, {cwd, absolute: true});
47
+ const data = [];
48
+ for (const filePath of filePaths) {
49
+ const segments = filePath.split(path.sep); // Use path.sep for platform independence
50
+ // const segments = filePath.split('/');
51
+ const srcIndex = segments.findIndex((segment, index) => segment === src && segments[index + 1] === 'entities');
52
+ const parents = segments.slice(srcIndex + 2, -1).reverse(); // +2 to skip "${src}" and "entities"
53
+ if (parents.length > 0) {
54
+ const api = await loadEntityApiConfig(cwd, filePath);
55
+ data.push({filePath, parents, api});
56
+ } else {
57
+ console.log(`WARNING: "${filePath}" Is not a valid entity filePath, must be contained in a named src under "${src}/entities/", see "${src}/entities/agents" or "${src}/entities/customers" for examples.`);
58
+ }
59
+ }
60
+
61
+ const specialEntities = ['agents'];
62
+
63
+ for (const {filePath, parents, api} of data) {
64
+ let entityConfig = {};
65
+ // const fileName = filePath.split('/')?.pop()?.split('.')?.[0];
66
+ const fileName = path.basename(filePath, path.extname(filePath));
67
+ if (!fileName) throw new Error(`Invalid file name "${filePath}"`);
68
+ const isSpecial = specialEntities.some(se => parents.includes(se));
69
+ if ((fileName === 'index' || fileName === 'config') && !isSpecial) {
70
+ const entityConfigHandler = await requireProjectFile(filePath).then(mod => mod.default);
71
+ // Check if entityConfig is a function or object
72
+ const entityType = checkVariableType(entityConfigHandler);
73
+ switch (entityType) {
74
+ case 'async function':
75
+ case 'function':
76
+ entityConfig = await entityConfigHandler();
77
+ break;
78
+ case 'object':
79
+ entityConfig = entityConfigHandler;
80
+ break;
81
+ default:
82
+ throw new Error(`Invalid entity type (${entityType}) returned at "${filePath}"`);
83
+ }
84
+
85
+ // Validate entity configuration
86
+ const result = entityConfigurationSchema.safeParse(entityConfig, {path: ['entities', config.length]});
87
+ if (!result.success) {
88
+ result.error.source = filePath;
89
+ throw result.error;
90
+ }
91
+ } else if (isSpecial && (fileName === 'index' || fileName === 'config')) {
92
+ // If this is a special entity file, then ignore as we will capture it another method
93
+ continue;
94
+ }
95
+
96
+ // Validate project configuration
97
+ const entityProjectConfig = {
98
+ ...entityConfig,
99
+ entity: parents[0],
100
+ entities: parents.reverse(),
101
+ api
102
+ };
103
+ entityRootProjectConfigurationSchema.parse(entityProjectConfig);
104
+ const existingIndex = config.findIndex(c => c.entity === entityProjectConfig.entity);
105
+ if (existingIndex > -1) {
106
+ if (config[existingIndex].entities.length !== entityProjectConfig.entities.length) {
107
+ throw new Error(`Invalid entity configuration at "${filePath}", entity name mismatch`);
108
+ }
109
+ config[existingIndex] = {
110
+ ...config[existingIndex],
111
+ definitions: [
112
+ ...(config[existingIndex].definitions),
113
+ ...(entityProjectConfig.definitions || [])
114
+ ],
115
+ training: [
116
+ ...(config[existingIndex].training),
117
+ ...(entityProjectConfig.training || [])
118
+ ],
119
+ tests: [
120
+ ...(config[existingIndex].tests),
121
+ ...(entityProjectConfig.tests || [])
122
+ ],
123
+ api: (!config[existingIndex].api && !entityProjectConfig.api) ? null : {
124
+ ...(config[existingIndex].api || {}),
125
+ ...(entityProjectConfig.api || {})
126
+ }
127
+ }
128
+ } else {
129
+ config.push(entityProjectConfig);
130
+ }
131
+ }
132
+
133
+ // if (!config.some(c => c.entity === 'customers')) {
134
+ // throw new Error(`Missing required entity: "entities/customers" in ${src}`);
135
+ // }
136
+ // if (!config.some(c => c.entity === '[customer]')) {
137
+ // throw new Error(`Missing required entity: "entities/customers/[customer]"`);
138
+ // }
139
+
140
+ // Validate the config
141
+ entitiesRootProjectConfigurationSchema.parse(config);
142
+
143
+ return config;
144
+ }
145
+
@@ -0,0 +1,23 @@
1
+ import path from 'node:path';
2
+ import { pathToFileURL } from 'node:url';
3
+ import fss from 'node:fs';
4
+ import fs from 'node:fs/promises';
5
+
6
+ /**
7
+ * Loads the users local package.json, if they provide a package-s9-test.json, it will load that instead (used for scout9 internal testing)
8
+ * @param {cwd?: string}
9
+ * @returns {Promise<{isTest: boolean, pkg: {name: string; version: string; dependencies: Record<string, string>}}>}
10
+ */
11
+ export async function loadUserPackageJson({cwd = process.cwd()}) {
12
+ const packageJsonPath = path.resolve(cwd, './package.json');
13
+ const packageTestJsonPath = path.resolve(cwd, './package-s9-test.json');
14
+ const packageJsonUrl = pathToFileURL(packageJsonPath);
15
+ const packageTestJsonUrl = pathToFileURL(packageTestJsonPath);
16
+ const isTest = fss.existsSync(packageTestJsonUrl);
17
+ const targetPkgUrl = isTest ? packageTestJsonUrl : packageJsonUrl;
18
+ const pkg = JSON.parse(await fs.readFile(new URL(targetPkgUrl, import.meta.url), 'utf-8'));
19
+ return {
20
+ pkg,
21
+ isTest
22
+ }
23
+ }
@@ -0,0 +1,47 @@
1
+ import { globSync } from 'glob';
2
+ import { WorkflowConfigurationSchema, WorkflowsConfigurationSchema } from '../../runtime/index.js';
3
+
4
+
5
+ /**
6
+ * @returns {Promise<WorkflowsBuildConfig>}
7
+ */
8
+ export default async function loadWorkflowsConfig(
9
+ {
10
+ cwd = process.cwd(),
11
+ src = 'src',
12
+ // cb = (message) => {}
13
+ } = {}
14
+ ) {
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
+ .map((path) => {
18
+ const segments = path.split('/');
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
+ return {path, parents};
22
+ })
23
+ .filter(path => {
24
+ if (path.parents.length > 0) {
25
+ return true;
26
+ } else {
27
+ console.log(`WARNING: "${path}" Is not a valid entity path, must be contained in a named src under workflows/`);
28
+ }
29
+ })
30
+ .map(({path, parents}) => {
31
+
32
+ // Validate project configuration
33
+ /** @type {WorkflowBuildConfig} */
34
+ const workflowConfig = {
35
+ entity: parents[0],
36
+ entities: parents.reverse(),
37
+ }
38
+ WorkflowConfigurationSchema.parse(workflowConfig);
39
+
40
+ return workflowConfig;
41
+ });
42
+
43
+ // Validate the config
44
+ WorkflowsConfigurationSchema.parse(config);
45
+
46
+ return config;
47
+ }