synthos 0.1.0
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/LICENSE +21 -0
- package/README.md +71 -0
- package/bin/synthos.js +3 -0
- package/default-pages/[application].html +87 -0
- package/default-pages/[markdown].html +261 -0
- package/default-pages/[sidebar].html +89 -0
- package/default-pages/[split-application].html +133 -0
- package/default-pages/json_tools.html +176 -0
- package/default-scripts/android.terminal.json +7 -0
- package/default-scripts/linux-terminal.json +7 -0
- package/default-scripts/mac-terminal.json +7 -0
- package/default-scripts/windows-terminal.json +7 -0
- package/dist/files.d.ts +9 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +79 -0
- package/dist/files.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +9 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +59 -0
- package/dist/init.js.map +1 -0
- package/dist/pages.d.ts +6 -0
- package/dist/pages.d.ts.map +1 -0
- package/dist/pages.js +55 -0
- package/dist/pages.js.map +1 -0
- package/dist/scripts.d.ts +14 -0
- package/dist/scripts.d.ts.map +1 -0
- package/dist/scripts.js +103 -0
- package/dist/scripts.js.map +1 -0
- package/dist/service/createCompletePrompt.d.ts +4 -0
- package/dist/service/createCompletePrompt.d.ts.map +1 -0
- package/dist/service/createCompletePrompt.js +42 -0
- package/dist/service/createCompletePrompt.js.map +1 -0
- package/dist/service/generateImage.d.ts +32 -0
- package/dist/service/generateImage.d.ts.map +1 -0
- package/dist/service/generateImage.js +71 -0
- package/dist/service/generateImage.js.map +1 -0
- package/dist/service/index.d.ts +8 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +24 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/requiresSettings.d.ts +3 -0
- package/dist/service/requiresSettings.d.ts.map +1 -0
- package/dist/service/requiresSettings.js +24 -0
- package/dist/service/requiresSettings.js.map +1 -0
- package/dist/service/server.d.ts +4 -0
- package/dist/service/server.d.ts.map +1 -0
- package/dist/service/server.js +26 -0
- package/dist/service/server.js.map +1 -0
- package/dist/service/transformPage.d.ts +11 -0
- package/dist/service/transformPage.d.ts.map +1 -0
- package/dist/service/transformPage.js +119 -0
- package/dist/service/transformPage.js.map +1 -0
- package/dist/service/useApiRoutes.d.ts +4 -0
- package/dist/service/useApiRoutes.d.ts.map +1 -0
- package/dist/service/useApiRoutes.js +95 -0
- package/dist/service/useApiRoutes.js.map +1 -0
- package/dist/service/useDataRoutes.d.ts +4 -0
- package/dist/service/useDataRoutes.d.ts.map +1 -0
- package/dist/service/useDataRoutes.js +98 -0
- package/dist/service/useDataRoutes.js.map +1 -0
- package/dist/service/usePageRoutes.d.ts +5 -0
- package/dist/service/usePageRoutes.d.ts.map +1 -0
- package/dist/service/usePageRoutes.js +132 -0
- package/dist/service/usePageRoutes.js.map +1 -0
- package/dist/settings.d.ts +13 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +55 -0
- package/dist/settings.js.map +1 -0
- package/dist/synthos-cli.d.ts +2 -0
- package/dist/synthos-cli.d.ts.map +1 -0
- package/dist/synthos-cli.js +43 -0
- package/dist/synthos-cli.js.map +1 -0
- package/images/home.png +0 -0
- package/images/page-management.png +0 -0
- package/images/settings.png +0 -0
- package/images/synthos-square.png +0 -0
- package/package.json +58 -0
- package/required-pages/apis.html +347 -0
- package/required-pages/home.html +83 -0
- package/required-pages/pages.html +135 -0
- package/required-pages/scripts.html +335 -0
- package/required-pages/settings.html +158 -0
- package/src/files.ts +49 -0
- package/src/index.ts +6 -0
- package/src/init.ts +66 -0
- package/src/pages.ts +55 -0
- package/src/scripts.ts +130 -0
- package/src/service/createCompletePrompt.ts +40 -0
- package/src/service/generateImage.ts +101 -0
- package/src/service/index.ts +7 -0
- package/src/service/requiresSettings.ts +22 -0
- package/src/service/server.ts +26 -0
- package/src/service/transformPage.ts +135 -0
- package/src/service/useApiRoutes.ts +95 -0
- package/src/service/useDataRoutes.ts +97 -0
- package/src/service/usePageRoutes.ts +147 -0
- package/src/settings.ts +60 -0
- package/src/synthos-cli.ts +38 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Application } from 'express';
|
|
2
|
+
import { SynthOSConfig } from "../init";
|
|
3
|
+
import { checkIfExists, deleteFile, ensureFolderExists, listFiles, loadFile, saveFile } from "../files";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { v4 } from "uuid";
|
|
6
|
+
import { clearCachedScripts } from '../scripts';
|
|
7
|
+
|
|
8
|
+
export function useDataRoutes(config: SynthOSConfig, app: Application): void {
|
|
9
|
+
// Retrieve all rows from a table
|
|
10
|
+
app.get('/api/data/:table', async (req, res) => {
|
|
11
|
+
// Get list of row ids
|
|
12
|
+
const { table } = req.params;
|
|
13
|
+
const folder = await tableFolder(config, table);
|
|
14
|
+
const ids = (await listFiles(folder)).filter(f => f.endsWith('.json')).map(f => f.replace('.json', ''));
|
|
15
|
+
|
|
16
|
+
// Enumerate all rows
|
|
17
|
+
const rows: Record<string, any>[] = [];
|
|
18
|
+
for (const id of ids) {
|
|
19
|
+
const file = recordFile(folder, id);
|
|
20
|
+
try {
|
|
21
|
+
const row = JSON.parse(await loadFile(file));
|
|
22
|
+
row.id = id;
|
|
23
|
+
rows.push(row);
|
|
24
|
+
} catch (err: unknown) {
|
|
25
|
+
console.error(err);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Return rows
|
|
30
|
+
res.json(rows);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Retrieve a single row from a table
|
|
34
|
+
app.get('/api/data/:table/:id', async (req, res) => {
|
|
35
|
+
const { table, id } = req.params;
|
|
36
|
+
const folder = await tableFolder(config, table);
|
|
37
|
+
const file = recordFile(folder, id);
|
|
38
|
+
try {
|
|
39
|
+
const row = JSON.parse(await loadFile(file));
|
|
40
|
+
row.id = id;
|
|
41
|
+
res.json(row);
|
|
42
|
+
} catch (err: unknown) {
|
|
43
|
+
res.json({});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Upsert a single row into a table
|
|
48
|
+
app.post('/api/data/:table', async (req, res) => {
|
|
49
|
+
const { table } = req.params;
|
|
50
|
+
const id = req.body.id ?? v4();
|
|
51
|
+
const folder = await tableFolder(config, table);
|
|
52
|
+
const file = recordFile(folder, id);
|
|
53
|
+
try {
|
|
54
|
+
const row = { ...req.body, id };
|
|
55
|
+
await ensureFolderExists(folder);
|
|
56
|
+
await saveFile(file, JSON.stringify(row, null, 4));
|
|
57
|
+
if (table === 'scripts') {
|
|
58
|
+
clearCachedScripts();
|
|
59
|
+
}
|
|
60
|
+
res.json(row);
|
|
61
|
+
} catch (err: unknown) {
|
|
62
|
+
console.error(err);
|
|
63
|
+
res.status(500).send((err as Error).message);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Delete a single row from a table
|
|
68
|
+
app.delete('/api/data/:table/:id', async (req, res) => {
|
|
69
|
+
const { table, id } = req.params;
|
|
70
|
+
const folder = await tableFolder(config, table);
|
|
71
|
+
const file = recordFile(folder, id);
|
|
72
|
+
try {
|
|
73
|
+
if (await checkIfExists(file)) {
|
|
74
|
+
await deleteFile(file);
|
|
75
|
+
|
|
76
|
+
// Clear cached scripts
|
|
77
|
+
if (table === 'scripts') {
|
|
78
|
+
clearCachedScripts();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
res.json({ success: true });
|
|
82
|
+
} catch (err: unknown) {
|
|
83
|
+
console.error(err);
|
|
84
|
+
res.status(500).send((err as Error).message);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function tableFolder(config: SynthOSConfig, table: string): Promise<string> {
|
|
90
|
+
const folder = path.join(config.pagesFolder, table);
|
|
91
|
+
await ensureFolderExists(folder);
|
|
92
|
+
return folder;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function recordFile(folder: string, id: string): string {
|
|
96
|
+
return path.join(folder, `${id}.json`);
|
|
97
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { loadPageState, normalizePageName, savePageState, updatePageState } from "../pages";
|
|
2
|
+
import { hasConfiguredSettings, loadSettings } from "../settings";
|
|
3
|
+
import { Application } from 'express';
|
|
4
|
+
import { transformPage, transformPageAsObject } from "./transformPage";
|
|
5
|
+
import { SynthOSConfig } from "../init";
|
|
6
|
+
import { createCompletePrompt } from "./createCompletePrompt";
|
|
7
|
+
import { completePrompt } from "agentm-core";
|
|
8
|
+
|
|
9
|
+
const HOME_PAGE_ROUTE = '/home';
|
|
10
|
+
const PAGE_NOT_FOUND = 'Page not found';
|
|
11
|
+
|
|
12
|
+
export function usePageRoutes(config: SynthOSConfig, app: Application): void {
|
|
13
|
+
// Redirect / to /home page
|
|
14
|
+
app.get('/', (req, res) => res.redirect(HOME_PAGE_ROUTE));
|
|
15
|
+
|
|
16
|
+
// Page retrieval
|
|
17
|
+
app.get('/:page', async (req, res) => {
|
|
18
|
+
// Redirect if settings not configured
|
|
19
|
+
const { page } = req.params;
|
|
20
|
+
const isConfigured = await hasConfiguredSettings(config.pagesFolder);
|
|
21
|
+
if (!isConfigured && page !== 'settings') {
|
|
22
|
+
res.redirect('/settings');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Ensure page exists
|
|
27
|
+
const pageState = await loadPageWithFallback(page, config, false);
|
|
28
|
+
if (!pageState) {
|
|
29
|
+
res.status(404).send(PAGE_NOT_FOUND);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
res.send(pageState);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Page reset
|
|
37
|
+
app.get('/:page/reset', async (req, res) => {
|
|
38
|
+
// Redirect if settings not configured
|
|
39
|
+
const { page } = req.params;
|
|
40
|
+
const isConfigured = await hasConfiguredSettings(config.pagesFolder);
|
|
41
|
+
if (!isConfigured) {
|
|
42
|
+
res.redirect('/settings');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Ensure page exists
|
|
47
|
+
const pageState = await loadPageWithFallback(page, config, true);
|
|
48
|
+
if (!pageState) {
|
|
49
|
+
res.status(404).send(PAGE_NOT_FOUND);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
res.redirect(`/${page}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Page save
|
|
57
|
+
app.get('/:page/save', async (req, res) => {
|
|
58
|
+
// Redirect if settings not configured
|
|
59
|
+
const { page } = req.params;
|
|
60
|
+
const isConfigured = await hasConfiguredSettings(config.pagesFolder);
|
|
61
|
+
if (!isConfigured) {
|
|
62
|
+
res.redirect('/settings');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Normalize and validate save-as parameter
|
|
67
|
+
const saveAs = normalizePageName(req.query['name'] as string);
|
|
68
|
+
if (!saveAs) {
|
|
69
|
+
res.status(400).send('Invalid or missing name parameter');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Load page state
|
|
74
|
+
const pageState = await loadPageWithFallback(page, config, false);
|
|
75
|
+
if (!pageState) {
|
|
76
|
+
res.status(404).send(PAGE_NOT_FOUND);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Save as new page and redirect to saved page
|
|
81
|
+
await savePageState(config.pagesFolder, saveAs, pageState);
|
|
82
|
+
res.redirect(`/${saveAs}`);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Page transformation
|
|
86
|
+
app.post('/:page', async (req, res) => {
|
|
87
|
+
try {
|
|
88
|
+
// Ensure settings configured
|
|
89
|
+
const { page } = req.params;
|
|
90
|
+
const isConfigured = await hasConfiguredSettings(config.pagesFolder);
|
|
91
|
+
if (!isConfigured) {
|
|
92
|
+
res.status(400).send('Settings not configured');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Ensure page exists
|
|
97
|
+
const pageState = await loadPageWithFallback(page, config, false);
|
|
98
|
+
if (!pageState) {
|
|
99
|
+
res.status(404).send(PAGE_NOT_FOUND);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Get required and optional parameters
|
|
104
|
+
const { message } = req.body; // Extract the message from the request body
|
|
105
|
+
if (typeof message !== 'string') {
|
|
106
|
+
res.status(400).send('Invalid or missing message parameter');
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Create model instance
|
|
111
|
+
const innerCompletePrompt = await createCompletePrompt(config.pagesFolder, req.body.model);
|
|
112
|
+
const completePrompt: completePrompt = (args) => {
|
|
113
|
+
// console.log(`SYSTEM:\n${args.system!.content}`);
|
|
114
|
+
// console.log(`PROMPT:\n${args.prompt!.content}`);
|
|
115
|
+
return innerCompletePrompt(args);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
// Transform and cache updated page
|
|
120
|
+
const pagesFolder = config.pagesFolder;
|
|
121
|
+
const { maxTokens, instructions, model } = await loadSettings(config.pagesFolder);
|
|
122
|
+
const result = model.startsWith('gpt-') ?
|
|
123
|
+
await transformPageAsObject({ pagesFolder, pageState, message, maxTokens, instructions, completePrompt }) :
|
|
124
|
+
await transformPage({ pagesFolder, pageState, message, maxTokens, instructions, completePrompt });
|
|
125
|
+
if (result.completed) {
|
|
126
|
+
updatePageState(page, result.value!);
|
|
127
|
+
res.send(result.value!);
|
|
128
|
+
} else {
|
|
129
|
+
throw result.error;
|
|
130
|
+
}
|
|
131
|
+
} catch (err: unknown) {
|
|
132
|
+
console.error(err);
|
|
133
|
+
res.status(500).send((err as Error).message);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function loadPageWithFallback(page: string, config: SynthOSConfig, reset: boolean): Promise<string|undefined> {
|
|
139
|
+
// Try primary pages folder first
|
|
140
|
+
const pageState = await loadPageState(config.pagesFolder, page, reset);
|
|
141
|
+
if (pageState) {
|
|
142
|
+
return pageState;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Try fallback pages folder second
|
|
146
|
+
return loadPageState(config.requiredPagesFolder, page, reset);
|
|
147
|
+
}
|
package/src/settings.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {checkIfExists, loadFile, saveFile} from './files';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
let _settings: Partial<Settings>|undefined;
|
|
5
|
+
|
|
6
|
+
export interface Settings {
|
|
7
|
+
serviceApiKey: string;
|
|
8
|
+
model: string;
|
|
9
|
+
maxTokens: number;
|
|
10
|
+
imageQuality: 'standard' | 'hd';
|
|
11
|
+
instructions?: string;
|
|
12
|
+
logCompletions?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const DefaultSettings: Settings = {
|
|
16
|
+
serviceApiKey: '',
|
|
17
|
+
model: '',
|
|
18
|
+
maxTokens: 8000,
|
|
19
|
+
imageQuality: 'standard',
|
|
20
|
+
instructions: '',
|
|
21
|
+
logCompletions: false
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function hasConfiguredSettings(folder: string): Promise<boolean> {
|
|
25
|
+
const settings = await loadSettings(folder);
|
|
26
|
+
if (typeof settings.serviceApiKey !== 'string' || settings.serviceApiKey.length == 0) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (typeof settings.model !== 'string' || settings.model.length == 0) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (typeof settings.maxTokens !== 'number' || settings.maxTokens <= 0) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function loadSettings(folder: string): Promise<Settings> {
|
|
40
|
+
if (_settings == undefined) {
|
|
41
|
+
// Check for file to exist
|
|
42
|
+
const filename = path.join(folder, 'settings.json');
|
|
43
|
+
if (await checkIfExists(filename)) {
|
|
44
|
+
try {
|
|
45
|
+
// Load and parse file
|
|
46
|
+
_settings = JSON.parse(await loadFile(filename));
|
|
47
|
+
} catch {
|
|
48
|
+
// Invalid JSON
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Return settings from file
|
|
54
|
+
return {...DefaultSettings, ..._settings};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function saveSettings(folder: string, settings: Partial<Settings>): Promise<void> {
|
|
58
|
+
_settings = {..._settings, ...settings};
|
|
59
|
+
await saveFile(path.join(folder, 'settings.json'), JSON.stringify(_settings, null, 4));
|
|
60
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import yargs from "yargs";
|
|
2
|
+
import { hideBin } from "yargs/helpers";
|
|
3
|
+
import { server } from "./service";
|
|
4
|
+
import { createConfig, init } from "./init";
|
|
5
|
+
|
|
6
|
+
const dynamicImport = new Function('specifier', `return import(specifier)`);
|
|
7
|
+
|
|
8
|
+
export async function run() {
|
|
9
|
+
await yargs(hideBin(process.argv))
|
|
10
|
+
.scriptName('synthos')
|
|
11
|
+
.command('start', `Starts the SynthOS server.`, (yargs) => {
|
|
12
|
+
return yargs
|
|
13
|
+
.option('port', {
|
|
14
|
+
describe: `The port number to use.`,
|
|
15
|
+
type: 'number',
|
|
16
|
+
default: 4242
|
|
17
|
+
})
|
|
18
|
+
.option('pages', {
|
|
19
|
+
describe: `Include default pages when initializing a new .synthos folder.`,
|
|
20
|
+
type: 'boolean',
|
|
21
|
+
default: true
|
|
22
|
+
})
|
|
23
|
+
.demandOption([]);
|
|
24
|
+
}, async (args) => {
|
|
25
|
+
const config = createConfig();
|
|
26
|
+
await init(config, args.pages);
|
|
27
|
+
await server(config).listen(args.port, async () => {
|
|
28
|
+
console.log(`SynthOS server is running on http://localhost:${args.port}`);
|
|
29
|
+
|
|
30
|
+
// Open using default browser
|
|
31
|
+
const open = await dynamicImport('open');
|
|
32
|
+
open.default(`http://localhost:${args.port}`);
|
|
33
|
+
});
|
|
34
|
+
})
|
|
35
|
+
.help()
|
|
36
|
+
.demandCommand()
|
|
37
|
+
.parseAsync();
|
|
38
|
+
}
|