@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.
- package/README.md +32 -0
- package/dist/{index-92deaa5f.cjs → exports-e7d51b70.cjs} +46618 -4591
- package/dist/index.cjs +58 -15
- package/dist/{multipart-parser-090f08a9.cjs → multipart-parser-e09a67c9.cjs} +13 -7
- package/dist/spirits-3b603262.cjs +1218 -0
- package/dist/spirits.cjs +9 -0
- package/dist/testing-tools.cjs +48 -0
- package/package.json +30 -8
- package/src/cli.js +162 -69
- package/src/core/config/agents.js +300 -7
- package/src/core/config/entities.js +58 -28
- package/src/core/config/index.js +37 -15
- package/src/core/config/project.js +160 -6
- package/src/core/config/workflow.js +13 -12
- package/src/core/data.js +27 -0
- package/src/core/index.js +386 -137
- package/src/core/sync.js +71 -0
- package/src/core/templates/Dockerfile +22 -0
- package/src/core/templates/app.js +453 -0
- package/src/core/templates/project-files.js +36 -0
- package/src/core/templates/template-package.json +13 -0
- package/src/exports.js +21 -17
- package/src/platform.js +189 -33
- package/src/public.d.ts.text +330 -0
- package/src/report.js +117 -0
- package/src/runtime/client/api.js +56 -159
- package/src/runtime/client/config.js +60 -11
- package/src/runtime/client/entity.js +19 -6
- package/src/runtime/client/index.js +5 -3
- package/src/runtime/client/message.js +13 -3
- package/src/runtime/client/platform.js +86 -0
- package/src/runtime/client/{agent.js → users.js} +35 -3
- package/src/runtime/client/utils.js +10 -9
- package/src/runtime/client/workflow.js +131 -9
- package/src/runtime/entry.js +2 -2
- package/src/testing-tools/dev.js +373 -0
- package/src/testing-tools/index.js +1 -0
- package/src/testing-tools/mocks.js +37 -5
- package/src/testing-tools/spirits.js +530 -0
- package/src/utils/audio-buffer.js +16 -0
- package/src/utils/audio-type.js +27 -0
- package/src/utils/configs/agents.js +68 -0
- package/src/utils/configs/entities.js +145 -0
- package/src/utils/configs/project.js +23 -0
- package/src/utils/configs/workflow.js +47 -0
- package/src/utils/file-type.js +569 -0
- package/src/utils/file.js +158 -0
- package/src/utils/glob.js +30 -0
- package/src/utils/image-buffer.js +23 -0
- package/src/utils/image-type.js +39 -0
- package/src/utils/index.js +1 -0
- package/src/utils/is-svg.js +37 -0
- package/src/utils/logger.js +111 -0
- package/src/utils/module.js +14 -25
- package/src/utils/project-templates.js +191 -0
- package/src/utils/project.js +387 -0
- package/src/utils/video-type.js +29 -0
- package/types/index.d.ts +7588 -206
- package/types/index.d.ts.map +97 -22
- package/dist/index-1b8d7dd2.cjs +0 -49555
- package/dist/index-2ccb115e.cjs +0 -49514
- package/dist/index-66b06a30.cjs +0 -49549
- package/dist/index-bc029a1d.cjs +0 -49528
- package/dist/index-d9a93523.cjs +0 -49527
- package/dist/multipart-parser-1508046a.cjs +0 -413
- package/dist/multipart-parser-7007403a.cjs +0 -413
- package/dist/multipart-parser-70c32c1d.cjs +0 -413
- package/dist/multipart-parser-71dec101.cjs +0 -413
- package/dist/multipart-parser-f15bf2e0.cjs +0 -414
- 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
|
+
}
|