synthos 0.6.0 → 0.7.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/README.md +33 -1
- package/default-pages/app_builder.html +40 -0
- package/default-pages/app_builder.json +1 -0
- package/default-pages/json_tools.html +89 -159
- package/default-pages/json_tools.json +1 -0
- package/default-pages/my_notes.html +33 -0
- package/default-pages/my_notes.json +12 -0
- package/default-pages/neon_asteroids.html +77 -0
- package/default-pages/neon_asteroids.json +12 -0
- package/default-pages/sidebar_builder.html +49 -0
- package/default-pages/sidebar_builder.json +1 -0
- package/default-pages/solar_explorer.html +1956 -0
- package/default-pages/solar_explorer.json +12 -0
- package/default-pages/solar_tutorial.html +476 -0
- package/default-pages/solar_tutorial.json +1 -0
- package/default-pages/two-panel_builder.html +66 -0
- package/default-pages/two-panel_builder.json +1 -0
- package/dist/connectors/index.d.ts +3 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +6 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/registry.d.ts +3 -0
- package/dist/connectors/registry.d.ts.map +1 -0
- package/dist/connectors/registry.js +100 -0
- package/dist/connectors/registry.js.map +1 -0
- package/dist/connectors/types.d.ts +61 -0
- package/dist/connectors/types.d.ts.map +1 -0
- package/dist/connectors/types.js +3 -0
- package/dist/connectors/types.js.map +1 -0
- package/dist/files.d.ts +2 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +12 -1
- package/dist/files.js.map +1 -1
- package/dist/init.d.ts +8 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +155 -3
- package/dist/init.js.map +1 -1
- package/dist/migrations.d.ts +11 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +281 -0
- package/dist/migrations.js.map +1 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +10 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/providers.d.ts +7 -0
- package/dist/models/providers.d.ts.map +1 -0
- package/dist/models/providers.js +33 -0
- package/dist/models/providers.js.map +1 -0
- package/dist/models/types.d.ts +21 -0
- package/dist/models/types.d.ts.map +1 -0
- package/dist/models/types.js +3 -0
- package/dist/models/types.js.map +1 -0
- package/dist/pages.d.ts +21 -2
- package/dist/pages.d.ts.map +1 -1
- package/dist/pages.js +202 -23
- package/dist/pages.js.map +1 -1
- package/dist/scripts.js +2 -2
- package/dist/scripts.js.map +1 -1
- package/dist/service/createCompletePrompt.d.ts +3 -2
- package/dist/service/createCompletePrompt.d.ts.map +1 -1
- package/dist/service/createCompletePrompt.js +11 -16
- package/dist/service/createCompletePrompt.js.map +1 -1
- package/dist/service/debugLog.d.ts +11 -0
- package/dist/service/debugLog.d.ts.map +1 -0
- package/dist/service/debugLog.js +26 -0
- package/dist/service/debugLog.js.map +1 -0
- package/dist/service/modelInstructions.d.ts +7 -0
- package/dist/service/modelInstructions.d.ts.map +1 -0
- package/dist/service/modelInstructions.js +16 -0
- package/dist/service/modelInstructions.js.map +1 -0
- package/dist/service/requiresSettings.d.ts +2 -2
- package/dist/service/requiresSettings.d.ts.map +1 -1
- package/dist/service/requiresSettings.js.map +1 -1
- package/dist/service/server.d.ts.map +1 -1
- package/dist/service/server.js +15 -0
- package/dist/service/server.js.map +1 -1
- package/dist/service/transformPage.d.ts +81 -2
- package/dist/service/transformPage.d.ts.map +1 -1
- package/dist/service/transformPage.js +672 -82
- package/dist/service/transformPage.js.map +1 -1
- package/dist/service/useApiRoutes.d.ts.map +1 -1
- package/dist/service/useApiRoutes.js +579 -13
- package/dist/service/useApiRoutes.js.map +1 -1
- package/dist/service/useConnectorRoutes.d.ts +4 -0
- package/dist/service/useConnectorRoutes.d.ts.map +1 -0
- package/dist/service/useConnectorRoutes.js +389 -0
- package/dist/service/useConnectorRoutes.js.map +1 -0
- package/dist/service/useDataRoutes.d.ts.map +1 -1
- package/dist/service/useDataRoutes.js +83 -70
- package/dist/service/useDataRoutes.js.map +1 -1
- package/dist/service/usePageRoutes.d.ts.map +1 -1
- package/dist/service/usePageRoutes.js +243 -38
- package/dist/service/usePageRoutes.js.map +1 -1
- package/dist/settings.d.ts +33 -4
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +108 -15
- package/dist/settings.js.map +1 -1
- package/dist/synthos-cli.d.ts.map +1 -1
- package/dist/synthos-cli.js +11 -1
- package/dist/synthos-cli.js.map +1 -1
- package/dist/themes.d.ts +9 -0
- package/dist/themes.d.ts.map +1 -0
- package/dist/themes.js +64 -0
- package/dist/themes.js.map +1 -0
- package/package.json +5 -3
- package/required-pages/builder.html +74 -0
- package/required-pages/builder.json +1 -0
- package/required-pages/pages.html +169 -126
- package/required-pages/pages.json +1 -0
- package/required-pages/settings.html +812 -156
- package/required-pages/settings.json +1 -0
- package/required-pages/synthos_apis.html +272 -0
- package/required-pages/synthos_apis.json +1 -0
- package/required-pages/synthos_scripts.html +87 -0
- package/required-pages/synthos_scripts.json +1 -0
- package/src/connectors/index.ts +12 -0
- package/src/connectors/registry.ts +98 -0
- package/src/connectors/types.ts +68 -0
- package/src/files.ts +11 -0
- package/src/init.ts +151 -5
- package/src/migrations.ts +266 -0
- package/src/models/index.ts +2 -0
- package/src/models/providers.ts +33 -0
- package/src/models/types.ts +23 -0
- package/src/pages.ts +234 -26
- package/src/scripts.ts +2 -2
- package/src/service/createCompletePrompt.ts +14 -18
- package/src/service/debugLog.ts +17 -0
- package/src/service/modelInstructions.ts +14 -0
- package/src/service/requiresSettings.ts +3 -3
- package/src/service/server.ts +19 -2
- package/src/service/transformPage.ts +709 -88
- package/src/service/useApiRoutes.ts +632 -16
- package/src/service/useConnectorRoutes.ts +427 -0
- package/src/service/useDataRoutes.ts +87 -71
- package/src/service/usePageRoutes.ts +237 -44
- package/src/settings.ts +143 -20
- package/src/synthos-cli.ts +11 -1
- package/src/themes.ts +71 -0
- package/default-pages/[application].html +0 -95
- package/default-pages/[markdown].html +0 -271
- package/default-pages/[sidebar].html +0 -114
- package/default-pages/[split-application].html +0 -118
- package/default-pages/solar_system.html +0 -432
- package/default-pages/space_invaders.html +0 -617
- package/required-pages/apis.html +0 -362
- package/required-pages/home.html +0 -126
- package/required-pages/scripts.html +0 -350
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "title": "Settings", "categories": ["System"], "pinned": true, "showInAll": true, "pageVersion": 2, "mode": "locked" }
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS - APIs</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.dialog-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px;width:100%;max-width:800px;box-shadow:0 6px 25px var(--accent-glow);position:relative;z-index:1}.dialog-content{font-size:14px;color:rgba(224,224,224,.8);padding:15px 0;flex-grow:1;width:100%;max-width:800px;position:relative;z-index:1;overflow-y:auto}.api-section{margin:8px 0;border-radius:10px;width:100%;background:linear-gradient(135deg,rgba(102,126,234,.1) 0,rgba(118,75,162,.1) 100%);border:1px solid var(--border-color);overflow:hidden}.api-header{font-size:14px;cursor:pointer;background:linear-gradient(135deg,rgba(102,126,234,.2) 0,rgba(118,75,162,.2) 100%);padding:12px 15px;text-align:left;transition:.3s;color:var(--text-secondary);font-weight:500}.api-header:hover{background:linear-gradient(135deg,rgba(102,126,234,.3) 0,rgba(118,75,162,.3) 100%);color:var(--accent-tertiary)}.api-content{display:none;padding:12px 15px;background:rgba(15,15,35,.5);border-top:1px solid var(--border-color);font-size:13px;line-height:1.5}.api-input{margin-top:10px}.api-input input,.api-input select,.api-input textarea{width:100%;padding:10px 12px;margin-bottom:8px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s}.api-input input:focus,.api-input select:focus,.api-input textarea:focus{outline:0;border-color:var(--text-secondary);box-shadow:0 0 15px var(--accent-glow)}.api-input textarea{min-height:80px;resize:vertical}.api-input select{cursor:pointer}.api-input select option{background:var(--bg-tertiary);color:var(--text-primary)}.api-input button{padding:10px 18px;border:none;border-radius:20px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:500;font-size:13px;box-shadow:0 3px 15px var(--accent-glow)}.api-input button:hover{transform:translateY(-2px);box-shadow:0 5px 20px rgba(102,126,234,.5)}.api-input button:disabled{background:rgba(102,126,234,.3);cursor:not-allowed;transform:none;box-shadow:none}.api-output{margin-top:10px;min-height:100px;max-height:200px;background:rgba(15,15,35,.8);color:var(--accent-tertiary);padding:12px;border-radius:8px;overflow-y:auto;white-space:pre-wrap;font-family:'Courier New',Courier,monospace;font-size:12px;border:1px solid var(--border-color)}.generated-image{max-width:100%;height:auto;margin-top:10px;border-radius:8px;border:1px solid var(--border-color);box-shadow:0 4px 20px var(--accent-glow)}.light-mode .dialog-content{color:rgba(45,38,64,.8)}.light-mode .api-content{background:rgba(255,255,255,.5)}.light-mode .api-input input,.light-mode .api-input select,.light-mode .api-input textarea,.light-mode .api-output{background:rgba(255,255,255,.8)}</style>
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
11
|
+
<script id="page-info" src="/api/page-info.js?page=apis"></script>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div class="chat-panel" data-locked="true">
|
|
15
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
16
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
17
|
+
<div class="chat-message"><p><strong>SynthOS:</strong> Expand the individual API operations to test calls.</p></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="link-group" data-locked="true">
|
|
20
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
21
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
22
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
23
|
+
</div>
|
|
24
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
25
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
26
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
27
|
+
</form>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="viewer-panel" id="viewerPanel">
|
|
30
|
+
<div class="dialog-title">API Explorer</div>
|
|
31
|
+
<div class="dialog-content">
|
|
32
|
+
<div class="api-section">
|
|
33
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/data/:page/:table</div>
|
|
34
|
+
<div class="api-content">
|
|
35
|
+
This operation retrieves all rows from the specified page-scoped table. The response is an array of JSON objects, each representing a row in the table.
|
|
36
|
+
<div class="api-input">
|
|
37
|
+
<input type="text" placeholder="Page Name">
|
|
38
|
+
<input type="text" placeholder="Table Name">
|
|
39
|
+
<button onclick="callApi(event, 'GET', '/api/data/')">Submit</button>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="api-output"></div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="api-section">
|
|
45
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/data/:page/:table?limit=N&offset=N</div>
|
|
46
|
+
<div class="api-content">
|
|
47
|
+
Paginated variant. Returns a page of rows from the table. The response includes the items, total count, and whether more rows are available.
|
|
48
|
+
Response: { items: [...], total: number, offset: number, limit: number, hasMore: boolean }
|
|
49
|
+
<div class="api-input">
|
|
50
|
+
<input type="text" placeholder="Page Name">
|
|
51
|
+
<input type="text" placeholder="Table Name">
|
|
52
|
+
<input type="text" placeholder="Limit">
|
|
53
|
+
<input type="text" placeholder="Offset (optional)">
|
|
54
|
+
<button onclick="callPaginatedApi(event)">Submit</button>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="api-output"></div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="api-section">
|
|
60
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/data/:page/:table/:id</div>
|
|
61
|
+
<div class="api-content">
|
|
62
|
+
This operation retrieves a single row from the specified page-scoped table using the provided ID. The response is a JSON object representing the row.
|
|
63
|
+
<div class="api-input">
|
|
64
|
+
<input type="text" placeholder="Page Name">
|
|
65
|
+
<input type="text" placeholder="Table Name">
|
|
66
|
+
<input type="text" placeholder="ID">
|
|
67
|
+
<button onclick="callApi(event, 'GET', '/api/data/')">Submit</button>
|
|
68
|
+
</div>
|
|
69
|
+
<div class="api-output"></div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="api-section">
|
|
73
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/data/:page/:table</div>
|
|
74
|
+
<div class="api-content">
|
|
75
|
+
This operation saves a single row to the specified page-scoped table. The request should include a JSON object representing the row. The response indicates success.
|
|
76
|
+
<div class="api-input">
|
|
77
|
+
<input type="text" placeholder="Page Name">
|
|
78
|
+
<input type="text" placeholder="Table Name">
|
|
79
|
+
<textarea placeholder="JSON Data"></textarea>
|
|
80
|
+
<button onclick="callApi(event, 'POST', '/api/data/')">Submit</button>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="api-output"></div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="api-section">
|
|
86
|
+
<div class="api-header" onclick="toggleSection(this)">DELETE /api/data/:page/:table/:id</div>
|
|
87
|
+
<div class="api-content">
|
|
88
|
+
This operation deletes a single row from the specified page-scoped table using the provided ID. The response indicates success.
|
|
89
|
+
<div class="api-input">
|
|
90
|
+
<input type="text" placeholder="Page Name">
|
|
91
|
+
<input type="text" placeholder="Table Name">
|
|
92
|
+
<input type="text" placeholder="ID">
|
|
93
|
+
<button onclick="callApi(event, 'DELETE', '/api/data/')">Submit</button>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="api-output"></div>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="api-section">
|
|
99
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/pages</div>
|
|
100
|
+
<div class="api-content">
|
|
101
|
+
This operation retrieves a list of all available pages. The response is an array of page objects with name, title, categories, pinned, createdDate, lastModified, pageVersion, and mode.
|
|
102
|
+
<div class="api-input">
|
|
103
|
+
<button onclick="callApi(event, 'GET', '/api/pages')">Submit</button>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="api-output"></div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<div class="api-section">
|
|
109
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/pages/:name</div>
|
|
110
|
+
<div class="api-content">
|
|
111
|
+
This operation retrieves the metadata for a single page, including title, categories, pinned, createdDate, lastModified, pageVersion, and mode.
|
|
112
|
+
<div class="api-input">
|
|
113
|
+
<input type="text" placeholder="Page Name">
|
|
114
|
+
<button onclick="callPageApi(event, 'GET')">Submit</button>
|
|
115
|
+
</div>
|
|
116
|
+
<div class="api-output"></div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="api-section">
|
|
120
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/pages/:name</div>
|
|
121
|
+
<div class="api-content">
|
|
122
|
+
This operation merges metadata for a page. Send only the fields you want to update. Supported fields: title (string), categories (array of strings), pinned (boolean), mode ("unlocked" | "locked"). The lastModified timestamp is auto-set on each update. The createdDate and pageVersion fields are preserved and cannot be overridden.
|
|
123
|
+
<div class="api-input">
|
|
124
|
+
<input type="text" placeholder="Page Name">
|
|
125
|
+
<textarea placeholder="{ "title": "My Page", "categories": ["System"], "pinned": true, "mode": "unlocked" }"></textarea>
|
|
126
|
+
<button onclick="callPageApi(event, 'POST')">Submit</button>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="api-output"></div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="api-section">
|
|
132
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/pages/:name/pin</div>
|
|
133
|
+
<div class="api-content">
|
|
134
|
+
This operation toggles the pinned status for a page. The request body should include pinned (boolean).
|
|
135
|
+
<div class="api-input">
|
|
136
|
+
<input type="text" placeholder="Page Name">
|
|
137
|
+
<textarea placeholder="{ "pinned": true }"></textarea>
|
|
138
|
+
<button onclick="callPageApi(event, 'POST', '/pin')">Submit</button>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="api-output"></div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="api-section">
|
|
144
|
+
<div class="api-header" onclick="toggleSection(this)">DELETE /api/pages/:name</div>
|
|
145
|
+
<div class="api-content">
|
|
146
|
+
This operation deletes a user page. Required (system) pages cannot be deleted. Returns 400 if the page is a required page, or 404 if not found.
|
|
147
|
+
<div class="api-input">
|
|
148
|
+
<input type="text" placeholder="Page Name">
|
|
149
|
+
<button onclick="callPageApi(event, 'DELETE')">Submit</button>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="api-output"></div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="api-section">
|
|
155
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/pages/:name/copy</div>
|
|
156
|
+
<div class="api-content">
|
|
157
|
+
This operation copies a page to a new name. The source page can be a user page or a required (system) page. The request body should include name (string, required), title (string, optional), and categories (array of strings, optional). Returns 409 if the target page already exists.
|
|
158
|
+
<div class="api-input">
|
|
159
|
+
<input type="text" placeholder="Page Name">
|
|
160
|
+
<textarea placeholder="{ "name": "new-page-name", "title": "My Copy", "categories": ["Tools"] }"></textarea>
|
|
161
|
+
<button onclick="callPageApi(event, 'POST', '/copy')">Submit</button>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="api-output"></div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="api-section">
|
|
167
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/search/web</div>
|
|
168
|
+
<div class="api-content">
|
|
169
|
+
This operation searches the web using Brave Search and returns a list of results. Requires Brave Search to be enabled in Settings > Connectors.
|
|
170
|
+
<div class="api-input">
|
|
171
|
+
<input type="text" placeholder="Search query" id="webSearchQuery">
|
|
172
|
+
<input type="number" placeholder="Count (optional, 1-20)" id="webSearchCount" min="1" max="20">
|
|
173
|
+
<button onclick="webSearch(event)">Search</button>
|
|
174
|
+
</div>
|
|
175
|
+
<div class="api-output"></div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<div class="api-section">
|
|
179
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/generate/image</div>
|
|
180
|
+
<div class="api-content">
|
|
181
|
+
This operation generates an image based on a prompt. You can specify the shape and style of the image.
|
|
182
|
+
<div class="api-input">
|
|
183
|
+
<input type="text" placeholder="Prompt" id="imagePrompt">
|
|
184
|
+
<select id="imageShape">
|
|
185
|
+
<option value="square">Square</option>
|
|
186
|
+
<option value="portrait">Portrait</option>
|
|
187
|
+
<option value="landscape">Landscape</option>
|
|
188
|
+
</select>
|
|
189
|
+
<select id="imageStyle">
|
|
190
|
+
<option value="vivid">Vivid</option>
|
|
191
|
+
<option value="natural">Natural</option>
|
|
192
|
+
</select>
|
|
193
|
+
<button onclick="generateImage(event)">Generate Image</button>
|
|
194
|
+
</div>
|
|
195
|
+
<div class="api-output"></div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
<div class="api-section">
|
|
199
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/generate/completion</div>
|
|
200
|
+
<div class="api-content">
|
|
201
|
+
This operation generates a text completion based on a prompt. You can optionally specify the temperature for controlling randomness.
|
|
202
|
+
<div class="api-input">
|
|
203
|
+
<textarea placeholder="Prompt" id="completionPrompt"></textarea>
|
|
204
|
+
<input type="number" placeholder="Temperature (optional)" id="completionTemperature" step="0.1" min="0" max="1">
|
|
205
|
+
<button onclick="generateCompletion(event)">Generate Completion</button>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="api-output"></div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="api-section">
|
|
211
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/scripts/:id</div>
|
|
212
|
+
<div class="api-content">
|
|
213
|
+
This operation executes a script with the specified ID and passes in the provided variables. The response contains the output of the script execution.
|
|
214
|
+
<div class="api-input">
|
|
215
|
+
<input type="text" placeholder="Script ID" id="scriptId">
|
|
216
|
+
<textarea placeholder="Variables (JSON format)" id="scriptVariables"></textarea>
|
|
217
|
+
<button onclick="executeScript(event)">Execute Script</button>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="api-output"></div>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div class="api-section">
|
|
223
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/connectors</div>
|
|
224
|
+
<div class="api-content">
|
|
225
|
+
Lists all available connectors with their configuration status. Supports optional category and id query filters.
|
|
226
|
+
<div class="api-input">
|
|
227
|
+
<input type="text" placeholder="Category (optional)" id="connectorListCategory">
|
|
228
|
+
<button onclick="listConnectors(event)">Submit</button>
|
|
229
|
+
</div>
|
|
230
|
+
<div class="api-output"></div>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
<div class="api-section">
|
|
234
|
+
<div class="api-header" onclick="toggleSection(this)">GET /api/connectors/:id</div>
|
|
235
|
+
<div class="api-content">
|
|
236
|
+
Retrieves full detail for a single connector, including its definition and whether it is configured and enabled.
|
|
237
|
+
<div class="api-input">
|
|
238
|
+
<input type="text" placeholder="Connector ID" id="connectorDetailId">
|
|
239
|
+
<button onclick="getConnectorDetail(event)">Submit</button>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="api-output"></div>
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
<div class="api-section">
|
|
245
|
+
<div class="api-header" onclick="toggleSection(this)">POST /api/connectors (proxy call)</div>
|
|
246
|
+
<div class="api-content">
|
|
247
|
+
Proxies a request through a configured connector. The connector attaches authentication automatically based on its auth strategy.
|
|
248
|
+
<div class="api-input">
|
|
249
|
+
<input type="text" placeholder="Connector ID" id="connectorCallId">
|
|
250
|
+
<select id="connectorCallMethod">
|
|
251
|
+
<option value="GET">GET</option>
|
|
252
|
+
<option value="POST">POST</option>
|
|
253
|
+
<option value="PUT">PUT</option>
|
|
254
|
+
<option value="DELETE">DELETE</option>
|
|
255
|
+
</select>
|
|
256
|
+
<input type="text" placeholder="Path (e.g. /res/v1/web/search)" id="connectorCallPath">
|
|
257
|
+
<textarea placeholder="Query params JSON (optional, e.g. { "q": "test" })" id="connectorCallQuery"></textarea>
|
|
258
|
+
<textarea placeholder="Body JSON (optional)" id="connectorCallBody"></textarea>
|
|
259
|
+
<button onclick="callConnector(event)">Submit</button>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="api-output"></div>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
266
|
+
</div>
|
|
267
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
268
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
269
|
+
<script id="api-explorer">function toggleSection(header){const content=header.nextElementSibling;content.style.display="block"===content.style.display?"none":"block"}function callApi(event,method,endpoint){event.preventDefault();const button=event.target;button.disabled=!0;const inputs=event.target.parentElement.querySelectorAll("input, textarea");let url=endpoint,data={};inputs.forEach(input=>{"Page Name"===input.placeholder?url+=input.value:"Table Name"===input.placeholder||"ID"===input.placeholder?url+="/"+input.value:"JSON Data"===input.placeholder&&(data=JSON.parse(input.value))}),fetch(url,{method:method,headers:{"Content-Type":"application/json"},body:"POST"===method?JSON.stringify(data):null}).then(response=>response.json()).then(data=>{console.log("Success:",data),event.target.parentElement.nextElementSibling.textContent=JSON.stringify(data,null,2)}).catch(error=>{console.error("Error:",error),alert("API call failed! Check console for details.")}).finally(()=>{button.disabled=!1})}function callPaginatedApi(event){event.preventDefault();const button=event.target;button.disabled=!0;const inputs=event.target.parentElement.querySelectorAll("input");let page="",table="",limit="",offset="";inputs.forEach(input=>{"Page Name"===input.placeholder?page=input.value:"Table Name"===input.placeholder?table=input.value:"Limit"===input.placeholder?limit=input.value:"Offset (optional)"===input.placeholder&&(offset=input.value)});let url="/api/data/"+encodeURIComponent(page)+"/"+encodeURIComponent(table)+"?limit="+encodeURIComponent(limit);offset&&(url+="&offset="+encodeURIComponent(offset)),fetch(url).then(response=>response.json()).then(data=>{event.target.parentElement.nextElementSibling.textContent=JSON.stringify(data,null,2)}).catch(error=>{console.error("Error:",error),alert("API call failed! Check console for details.")}).finally(()=>{button.disabled=!1})}function callPageApi(event,method,suffix){event.preventDefault();const button=event.target;button.disabled=!0;const inputs=event.target.parentElement.querySelectorAll("input, textarea");let pageName="",data=null;inputs.forEach(input=>{"Page Name"===input.placeholder?pageName=input.value:"TEXTAREA"===input.tagName&&(data=JSON.parse(input.value))});const url="/api/pages/"+encodeURIComponent(pageName)+(suffix||"");fetch(url,{method:method,headers:{"Content-Type":"application/json"},body:"POST"===method?JSON.stringify(data):null}).then(response=>response.json()).then(data=>{event.target.parentElement.nextElementSibling.textContent=JSON.stringify(data,null,2)}).catch(error=>{console.error("Error:",error),alert("API call failed! Check console for details.")}).finally(()=>{button.disabled=!1})}function generateImage(event){event.preventDefault();const button=event.target;button.disabled=!0;const prompt=document.getElementById("imagePrompt").value,shape=document.getElementById("imageShape").value,style=document.getElementById("imageStyle").value,outputBox=event.target.parentElement.nextElementSibling;fetch("/api/generate/image",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:prompt,shape:shape,style:style})}).then(response=>response.json()).then(data=>{if(console.log("Success:",data),outputBox.textContent=JSON.stringify(data,null,2),data.url){const img=document.createElement("img");img.src=data.url,img.alt="Generated Image",img.className="generated-image",outputBox.appendChild(img)}}).catch(error=>{console.error("Error:",error),alert("Image generation failed! Check console for details.")}).finally(()=>{button.disabled=!1})}function generateCompletion(event){event.preventDefault();const button=event.target;button.disabled=!0;const prompt=document.getElementById("completionPrompt").value,temperature=document.getElementById("completionTemperature").value,outputBox=event.target.parentElement.nextElementSibling;fetch("/api/generate/completion",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({prompt:prompt,temperature:parseFloat(temperature)||void 0})}).then(response=>response.json()).then(data=>{console.log("Success:",data),outputBox.textContent=JSON.stringify(data,null,2)}).catch(error=>{console.error("Error:",error),alert("Completion generation failed! Check console for details.")}).finally(()=>{button.disabled=!1})}function webSearch(event){event.preventDefault();var button=event.target;button.disabled=!0;var query=document.getElementById("webSearchQuery").value,count=document.getElementById("webSearchCount").value,outputBox=event.target.parentElement.nextElementSibling,body={query:query};count&&(body.count=parseInt(count,10)),fetch("/api/search/web",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}).then(function(response){return response.json()}).then(function(data){outputBox.textContent=JSON.stringify(data,null,2)}).catch(function(error){console.error("Error:",error),alert("Web search failed! Check console for details.")}).finally(function(){button.disabled=!1})}function executeScript(event){event.preventDefault();const button=event.target;button.disabled=!0;const scriptId=document.getElementById("scriptId").value,variables=JSON.parse(document.getElementById("scriptVariables").value),outputBox=event.target.parentElement.nextElementSibling;fetch(`/api/scripts/${scriptId}`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(variables)}).then(response=>response.json()).then(data=>{console.log("Success:",data),outputBox.textContent=JSON.stringify(data,null,2)}).catch(error=>{console.error("Error:",error),alert("Script execution failed! Check console for details.")}).finally(()=>{button.disabled=!1})}function listConnectors(event){event.preventDefault();var button=event.target;button.disabled=!0;var category=document.getElementById("connectorListCategory").value;var url="/api/connectors";if(category)url+="?category="+encodeURIComponent(category);var outputBox=event.target.parentElement.nextElementSibling;fetch(url).then(function(r){return r.json()}).then(function(data){outputBox.textContent=JSON.stringify(data,null,2)}).catch(function(err){console.error("Error:",err);alert("Failed! Check console.")}).finally(function(){button.disabled=!1})}function getConnectorDetail(event){event.preventDefault();var button=event.target;button.disabled=!0;var id=document.getElementById("connectorDetailId").value;var outputBox=event.target.parentElement.nextElementSibling;fetch("/api/connectors/"+encodeURIComponent(id)).then(function(r){return r.json()}).then(function(data){outputBox.textContent=JSON.stringify(data,null,2)}).catch(function(err){console.error("Error:",err);alert("Failed! Check console.")}).finally(function(){button.disabled=!1})}function callConnector(event){event.preventDefault();var button=event.target;button.disabled=!0;var body={connector:document.getElementById("connectorCallId").value,method:document.getElementById("connectorCallMethod").value,path:document.getElementById("connectorCallPath").value};var queryVal=document.getElementById("connectorCallQuery").value;if(queryVal)try{body.query=JSON.parse(queryVal)}catch(e){}var bodyVal=document.getElementById("connectorCallBody").value;if(bodyVal)try{body.body=JSON.parse(bodyVal)}catch(e){}var outputBox=event.target.parentElement.nextElementSibling;fetch("/api/connectors",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}).then(function(r){return r.json()}).then(function(data){outputBox.textContent=JSON.stringify(data,null,2)}).catch(function(err){console.error("Error:",err);alert("Failed! Check console.")}).finally(function(){button.disabled=!1})}</script>
|
|
270
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
|
|
271
|
+
<script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
|
|
272
|
+
</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "title": "SynthOS APIs", "categories": ["System"], "pinned": false, "showInAll": false, "pageVersion": 2, "mode": "locked" }
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head>
|
|
2
|
+
<meta charset="UTF-8">
|
|
3
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
4
|
+
<title>SynthOS - Scripts</title>
|
|
5
|
+
<script id="theme-info" src="/api/theme-info.js" data-locked="true"></script>
|
|
6
|
+
<link id="theme-css" rel="stylesheet" href="/api/theme.css" data-locked="true">
|
|
7
|
+
<style>.application-title{font-size:22px;font-weight:700;min-height:var(--header-min-height);padding:var(--header-padding-vertical) var(--header-padding-horizontal);line-height:var(--header-line-height);display:flex;align-items:center;justify-content:center;box-sizing:border-box;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;border-radius:12px;width:100%;max-width:800px;box-shadow:0 6px 25px var(--accent-glow);position:relative;z-index:1}.application-content{font-size:14px;color:var(--text-secondary);padding:15px 0;flex-grow:1;width:100%;max-width:800px;position:relative;z-index:1;overflow-y:auto;display:flex;gap:20px}.script-list{width:200px;flex-shrink:0}.script-list ul{list-style:none;background:rgba(15,15,35,.6);border-radius:10px;border:1px solid var(--border-color);overflow:hidden;margin-bottom:10px;max-height:300px;overflow-y:auto}.script-list li{padding:12px 15px;cursor:pointer;transition:.3s;border-bottom:1px solid var(--border-color);color:var(--text-secondary)}.script-list li:last-child{border-bottom:none}.script-list li:hover{background:linear-gradient(135deg,rgba(102,126,234,.2) 0,rgba(118,75,162,.2) 100%);color:var(--accent-tertiary)}.script-list li.active{background:linear-gradient(135deg,rgba(102,126,234,.3) 0,rgba(118,75,162,.3) 100%);color:var(--accent-tertiary);box-shadow:inset 0 0 15px var(--accent-glow)}.add-script-btn,.delete-script-btn,.save-script-btn{width:100%;padding:12px 18px;border:none;border-radius:10px;background:linear-gradient(135deg,var(--accent-primary) 0,var(--accent-secondary) 100%);color:#fff;cursor:pointer;transition:.3s;font-weight:500;font-size:13px;box-shadow:0 3px 15px var(--accent-glow);margin-bottom:8px}.add-script-btn:hover,.save-script-btn:hover{transform:translateY(-2px);box-shadow:0 5px 20px rgba(102,126,234,.5)}.delete-script-btn{background:linear-gradient(135deg,#e74c3c 0,#c0392b 100%);box-shadow:0 3px 15px rgba(231,76,60,.3)}.delete-script-btn:hover{box-shadow:0 5px 20px rgba(231,76,60,.5);transform:translateY(-2px)}.script-detail{flex-grow:1;background:rgba(15,15,35,.6);border-radius:10px;border:1px solid var(--border-color);padding:20px}.script-detail input,.script-detail select{width:100%;padding:12px 15px;margin-bottom:12px;border-radius:8px;border:1px solid var(--border-color);background:rgba(15,15,35,.8);color:var(--text-primary);font-size:13px;transition:.3s}.script-detail input:focus,.script-detail select:focus{outline:0;border-color:var(--text-secondary);box-shadow:0 0 15px var(--accent-glow)}.script-detail select{cursor:pointer}.script-detail select option{background:var(--bg-tertiary);color:var(--text-primary)}.placeholder-message{color:var(--text-secondary);text-align:center;padding:40px 20px;line-height:1.6}.error-message{display:none;color:#e74c3c;font-size:12px;margin-bottom:12px;padding:8px 12px;background:rgba(231,76,60,.1);border-radius:6px;border:1px solid rgba(231,76,60,.3)}.instructions{margin-top:20px;padding:15px;background:linear-gradient(135deg,rgba(102,126,234,.1) 0,rgba(118,75,162,.1) 100%);border-radius:10px;border:1px solid var(--border-color)}.instructions h3{color:var(--accent-tertiary);margin:15px 0 8px;font-size:14px}.instructions h3:first-child{margin-top:0}.instructions p{color:var(--text-secondary);font-size:12px;line-height:1.6;margin-bottom:8px}.instructions code{background:rgba(138,43,226,.3);padding:2px 6px;border-radius:4px;font-family:'Courier New',Courier,monospace;color:var(--accent-tertiary);border:1px solid rgba(240,147,251,.3);font-size:11px}.light-mode .script-detail,.light-mode .script-list ul{background:rgba(255,255,255,.6)}.light-mode .script-detail input,.light-mode .script-detail select{background:rgba(255,255,255,.8);box-shadow:inset 0 2px 10px rgba(118,75,162,.05)}</style>
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js"></script>
|
|
9
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/14.1.1/marked.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/11.1.0/mermaid.min.js"></script>
|
|
11
|
+
<script id="page-info" src="/api/page-info.js?page=scripts"></script>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div class="chat-panel" data-locked="true">
|
|
15
|
+
<div class="chat-header" data-locked="true">SynthOS</div>
|
|
16
|
+
<div class="chat-messages" id="chatMessages" data-locked="true">
|
|
17
|
+
<div class="chat-message"><p><strong>SynthOS:</strong> Add or modify scripts that can be executed using the <code>/api/scripts/:id</code> API.</p></div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="link-group" data-locked="true">
|
|
20
|
+
<a href="#" id="saveLink" data-locked="true">Save</a>
|
|
21
|
+
<a href="/pages" id="pagesLink" data-locked="true">Pages</a>
|
|
22
|
+
<a href="#" id="resetLink" data-locked="true">Reset</a>
|
|
23
|
+
</div>
|
|
24
|
+
<form action="/" method="POST" id="chatForm" data-locked="true">
|
|
25
|
+
<input type="text" class="chat-input" id="chatInput" name="message" placeholder="Type a message..." data-locked="true">
|
|
26
|
+
<button type="submit" class="chat-submit" data-locked="true">Send</button>
|
|
27
|
+
</form>
|
|
28
|
+
</div>
|
|
29
|
+
<div class="viewer-panel" id="viewerPanel">
|
|
30
|
+
<div class="application-title">Script Editor</div>
|
|
31
|
+
<div class="application-content">
|
|
32
|
+
<div class="script-list">
|
|
33
|
+
<ul id="scriptList">
|
|
34
|
+
<!-- Scripts will be loaded here -->
|
|
35
|
+
</ul>
|
|
36
|
+
<button class="add-script-btn">Add New Script</button>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="script-detail">
|
|
39
|
+
<div id="placeholderMessage" class="placeholder-message">
|
|
40
|
+
Select an existing script to edit or add a new one using the "Add New Script" button.
|
|
41
|
+
</div>
|
|
42
|
+
<input type="text" id="scriptId" placeholder="Script ID" value="" style="display: none;">
|
|
43
|
+
<select id="scriptType" style="display: none;">
|
|
44
|
+
<option value="command">Command</option>
|
|
45
|
+
</select>
|
|
46
|
+
<input type="text" id="scriptCommand" placeholder="Script Command" style="display: none;">
|
|
47
|
+
<input type="text" id="scriptDescription" placeholder="Usage Description (optional)" style="display: none;">
|
|
48
|
+
<input type="text" id="scriptVariables" placeholder="Variables (optional, e.g., { city: string })" style="display: none;">
|
|
49
|
+
<div id="commandError" class="error-message">Script Command is required.</div>
|
|
50
|
+
<button class="save-script-btn" style="display: none;">Save Changes</button>
|
|
51
|
+
<button class="delete-script-btn" style="display: none;">Delete Script</button>
|
|
52
|
+
<div class="instructions">
|
|
53
|
+
<h3>Understanding SynthOS Scripts</h3>
|
|
54
|
+
<p>Scripts in SynthOS are powerful tools that allow you to define custom terminal commands for various tasks. When SynthOS executes a script, it runs the command and captures the console output, which is then returned for further processing or analysis.</p>
|
|
55
|
+
|
|
56
|
+
<h3>Creating Effective Scripts</h3>
|
|
57
|
+
<p>When writing a script, you can use any valid terminal command. For added flexibility, you can include {{variable}} placeholders, which SynthOS will replace with actual values during execution.</p>
|
|
58
|
+
|
|
59
|
+
<h3>Example: Weather Forecast Script</h3>
|
|
60
|
+
<p>Here's an example of an interesting script that SynthOS could use:</p>
|
|
61
|
+
<p><code>curl wttr.in/{{city}}?format=3</code></p>
|
|
62
|
+
<p>This script fetches a concise weather forecast for a specified city. SynthOS can call this script with different city names to get up-to-date weather information.</p>
|
|
63
|
+
|
|
64
|
+
<h3>Tips for Script Writing</h3>
|
|
65
|
+
<p>1. Keep commands concise and focused on a single task.</p>
|
|
66
|
+
<p>2. Use variables for dynamic inputs to make scripts more versatile.</p>
|
|
67
|
+
<p>3. Consider potential errors and how to handle them.</p>
|
|
68
|
+
<p>4. Test your scripts thoroughly to ensure they work as expected.</p>
|
|
69
|
+
<p>5. Provide a clear and concise usage description to help others understand how to use your script.</p>
|
|
70
|
+
<p>6. Define the expected variables in the Variables field to document the script's requirements.</p>
|
|
71
|
+
|
|
72
|
+
<h3>Using the Description Field</h3>
|
|
73
|
+
<p>The Description field allows you to provide a brief explanation of what SynthOS can use your script for.</p>
|
|
74
|
+
|
|
75
|
+
<h3>Defining Variables</h3>
|
|
76
|
+
<p>Use the Variables field to specify any input parameters your script expects. Format it as a JSON object, e.g., { city: string, days?: number }. This helps SynthOS know what inputs it needs to provide when running your script.</p>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
<div id="loadingOverlay" class="loading-overlay"><div class="spinner"></div></div>
|
|
81
|
+
</div>
|
|
82
|
+
<div id="instructions" style="display: none;" data-locked="true"></div>
|
|
83
|
+
<div id="thoughts" style="display: none;" data-locked="true"></div>
|
|
84
|
+
<script id="script-editor">const scriptList=document.getElementById("scriptList"),scriptId=document.getElementById("scriptId"),scriptType=document.getElementById("scriptType"),scriptCommand=document.getElementById("scriptCommand"),scriptDescription=document.getElementById("scriptDescription"),scriptVariables=document.getElementById("scriptVariables"),addScriptBtn=document.querySelector(".add-script-btn"),saveScriptBtn=document.querySelector(".save-script-btn"),deleteScriptBtn=document.querySelector(".delete-script-btn"),placeholderMessage=document.getElementById("placeholderMessage"),commandError=document.getElementById("commandError");let isNewScript=!1;function showScriptDetails(){placeholderMessage.style.display="none",scriptId.style.display="block",scriptType.style.display="block",scriptCommand.style.display="block",scriptDescription.style.display="block",scriptVariables.style.display="block",saveScriptBtn.style.display="block",deleteScriptBtn.style.display=isNewScript?"none":"block"}function hideScriptDetails(){placeholderMessage.style.display="block",scriptId.style.display="none",scriptType.style.display="none",scriptCommand.style.display="none",scriptDescription.style.display="none",scriptVariables.style.display="none",saveScriptBtn.style.display="none",deleteScriptBtn.style.display="none",commandError.style.display="none"}function formatId(id){return id.toLowerCase().replace(/\s+/g,"-")}function loadScripts(){fetch("/api/data/scripts/scripts").then(response=>response.json()).then(scripts=>{scriptList.innerHTML="",scripts.forEach(script=>{const li=document.createElement("li");li.textContent=script.id,li.dataset.id=script.id,li.dataset.type=script.type,li.dataset.command=script.command,li.dataset.description=script.description||"",li.dataset.variables=script.variables||"",scriptList.appendChild(li)})}).catch(error=>console.error("Error loading scripts:",error))}function saveScript(script){fetch("/api/data/scripts/scripts").then(response=>response.json()).then(scripts=>{scripts.find(s=>s.id===script.id)&&isNewScript?confirm("A script with this ID already exists. Do you want to overwrite it?")&&performSave(script):performSave(script)}).catch(error=>console.error("Error checking existing scripts:",error))}function performSave(script){fetch("/api/data/scripts/scripts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(script)}).then(response=>response.json()).then(savedScript=>{loadScripts(),isNewScript=!1,setTimeout(()=>{const newScriptElement=document.querySelector(`[data-id="${savedScript.id}"]`);newScriptElement&&newScriptElement.click()},100)}).catch(error=>console.error("Error saving script:",error))}function deleteScript(id){confirm("Are you sure you want to delete this script?")&&fetch(`/api/data/scripts/scripts/${id}`,{method:"DELETE"}).then(response=>response.json()).then(result=>{result.success?(loadScripts(),hideScriptDetails()):console.error("Failed to delete script.")}).catch(error=>console.error("Error deleting script:",error))}scriptList.addEventListener("click",e=>{"LI"===e.target.tagName&&(document.querySelectorAll(".script-list li").forEach(li=>li.classList.remove("active")),e.target.classList.add("active"),showScriptDetails(),scriptId.value=e.target.dataset.id,scriptType.value=e.target.dataset.type||"command",scriptCommand.value=e.target.dataset.command||"",scriptDescription.value=e.target.dataset.description||"",scriptVariables.value=e.target.dataset.variables||"",commandError.style.display="none",isNewScript=!1,deleteScriptBtn.style.display="block")}),addScriptBtn.addEventListener("click",()=>{const newId=formatId(`script-${Date.now()}`);showScriptDetails(),scriptId.value=newId,scriptType.value="command",scriptCommand.value="",scriptDescription.value="",scriptVariables.value="",commandError.style.display="none",document.querySelectorAll(".script-list li").forEach(li=>li.classList.remove("active")),isNewScript=!0,deleteScriptBtn.style.display="none"}),saveScriptBtn.addEventListener("click",()=>{scriptCommand.value.trim()?saveScript({id:formatId(scriptId.value),type:scriptType.value,command:scriptCommand.value,description:scriptDescription.value.trim()||void 0,variables:scriptVariables.value.trim()||void 0}):commandError.style.display="block"}),deleteScriptBtn.addEventListener("click",()=>{const id=scriptId.value;id&&deleteScript(id)}),scriptId.addEventListener("input",e=>{e.target.value=formatId(e.target.value)}),scriptCommand.addEventListener("input",()=>{commandError.style.display="none"}),loadScripts(),hideScriptDetails();</script>
|
|
85
|
+
<script id="page-helpers" src="/api/page-helpers.js?v=2" data-locked="true"></script>
|
|
86
|
+
<script id="page-script" src="/api/page-script.js?v=2" data-locked="true"></script>
|
|
87
|
+
</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "title": "SynthOS Scripts", "categories": ["System"], "pinned": false, "showInAll": false, "pageVersion": 2, "mode": "locked" }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { CONNECTOR_REGISTRY } from './registry';
|
|
2
|
+
export type {
|
|
3
|
+
AuthStrategy,
|
|
4
|
+
ConnectorField,
|
|
5
|
+
ConnectorDefinition,
|
|
6
|
+
ConnectorConfig,
|
|
7
|
+
ConnectorOAuthConfig,
|
|
8
|
+
ConnectorsConfig,
|
|
9
|
+
ConnectorSummary,
|
|
10
|
+
ConnectorDetail,
|
|
11
|
+
ConnectorCallRequest
|
|
12
|
+
} from './types';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { ConnectorDefinition } from './types';
|
|
2
|
+
|
|
3
|
+
export const CONNECTOR_REGISTRY: ConnectorDefinition[] = [
|
|
4
|
+
{
|
|
5
|
+
id: 'brave-search',
|
|
6
|
+
name: 'Brave Search',
|
|
7
|
+
category: 'Search',
|
|
8
|
+
description: 'Web search powered by the Brave Search API. Provides real-time search results from the web.',
|
|
9
|
+
baseUrl: 'https://api.search.brave.com',
|
|
10
|
+
authStrategy: 'header',
|
|
11
|
+
authKey: 'X-Subscription-Token',
|
|
12
|
+
fields: [
|
|
13
|
+
{ name: 'apiKey', label: 'API Key', type: 'password' }
|
|
14
|
+
],
|
|
15
|
+
hints: [
|
|
16
|
+
'Endpoint: GET /res/v1/web/search',
|
|
17
|
+
'Query params: q (required), count (1-20, default 10), country, freshness',
|
|
18
|
+
'Response: { web: { results: [{ title, url, description }] } }',
|
|
19
|
+
'Note: synthos.search.web() is a convenience wrapper — prefer it over raw connector calls for basic web search.'
|
|
20
|
+
].join('\n')
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 'elevenlabs',
|
|
24
|
+
name: 'ElevenLabs',
|
|
25
|
+
category: 'Audio',
|
|
26
|
+
description: 'AI-powered text-to-speech and voice synthesis. Generate natural-sounding audio from text.',
|
|
27
|
+
baseUrl: 'https://api.elevenlabs.io',
|
|
28
|
+
authStrategy: 'header',
|
|
29
|
+
authKey: 'xi-api-key',
|
|
30
|
+
fields: [
|
|
31
|
+
{ name: 'apiKey', label: 'API Key', type: 'password' }
|
|
32
|
+
],
|
|
33
|
+
hints: [
|
|
34
|
+
'List voices: GET /v1/voices → { voices: [{ voice_id, name, category }] }',
|
|
35
|
+
'Text-to-speech: POST /v1/text-to-speech/{voice_id}?output_format=mp3_44100_128',
|
|
36
|
+
' Request headers: Accept: audio/mpeg, Content-Type: application/json',
|
|
37
|
+
' Request body: { text: string, model_id: "eleven_multilingual_v2" }',
|
|
38
|
+
' Response: raw audio/mpeg binary — use resp.arrayBuffer() then new Blob([buf], {type:"audio/mpeg"}) and URL.createObjectURL() to play',
|
|
39
|
+
' IMPORTANT: The proxy returns raw binary, NOT JSON. Call fetch("/api/connectors", ...) directly instead of synthos.connectors.call() for TTS, since the helper parses JSON.',
|
|
40
|
+
'Default voice: "Rachel" (voice_id: 21m00Tcm4TlvDq8ikWAM) is a good general-purpose voice.',
|
|
41
|
+
'Max text length: 5000 characters per request.'
|
|
42
|
+
].join('\n')
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'stability-ai',
|
|
46
|
+
name: 'Stability AI',
|
|
47
|
+
category: 'Image',
|
|
48
|
+
description: 'AI image generation powered by Stable Diffusion. Create images from text prompts.',
|
|
49
|
+
baseUrl: 'https://api.stability.ai',
|
|
50
|
+
authStrategy: 'bearer',
|
|
51
|
+
authKey: 'Authorization',
|
|
52
|
+
fields: [
|
|
53
|
+
{ name: 'apiKey', label: 'API Key', type: 'password' }
|
|
54
|
+
],
|
|
55
|
+
hints: [
|
|
56
|
+
'Text-to-image: POST /v1/generation/stable-diffusion-xl-1024-v1-0/text-to-image',
|
|
57
|
+
' Request headers: Content-Type: application/json, Accept: application/json',
|
|
58
|
+
' Request body: { text_prompts: [{ text: string, weight: 1 }], cfg_scale: 7, steps: 30, width: 1024, height: 1024 }',
|
|
59
|
+
' Response: { artifacts: [{ base64: string, finishReason: string }] }',
|
|
60
|
+
' Display with: <img src="data:image/png;base64,{base64}">'
|
|
61
|
+
].join('\n')
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: 'instagram',
|
|
65
|
+
name: 'Instagram',
|
|
66
|
+
category: 'Social',
|
|
67
|
+
description: 'Post photos and videos to Instagram via the Instagram Graph API.',
|
|
68
|
+
baseUrl: 'https://graph.facebook.com/v21.0',
|
|
69
|
+
authStrategy: 'oauth2',
|
|
70
|
+
authKey: 'access_token',
|
|
71
|
+
authorizationUrl: 'https://www.facebook.com/v21.0/dialog/oauth',
|
|
72
|
+
tokenUrl: 'https://graph.facebook.com/v21.0/oauth/access_token',
|
|
73
|
+
scopes: [
|
|
74
|
+
'instagram_basic',
|
|
75
|
+
'instagram_content_publish',
|
|
76
|
+
'pages_show_list',
|
|
77
|
+
'pages_read_engagement'
|
|
78
|
+
],
|
|
79
|
+
fields: [
|
|
80
|
+
{ name: 'clientId', label: 'App ID', type: 'text' },
|
|
81
|
+
{ name: 'clientSecret', label: 'App Secret', type: 'password' }
|
|
82
|
+
],
|
|
83
|
+
hints: [
|
|
84
|
+
'Publishing flow (two-step):',
|
|
85
|
+
' 1. Create media container: POST /{ig-user-id}/media',
|
|
86
|
+
' Body (photo): { image_url, caption }',
|
|
87
|
+
' Body (reel/video): { video_url, caption, media_type: "REELS" }',
|
|
88
|
+
' Response: { id: "<container-id>" }',
|
|
89
|
+
' 2. Publish container: POST /{ig-user-id}/media_publish',
|
|
90
|
+
' Body: { creation_id: "<container-id>" }',
|
|
91
|
+
' Response: { id: "<media-id>" }',
|
|
92
|
+
'IMPORTANT: Do NOT include access_token in body or query params — the proxy attaches it automatically.',
|
|
93
|
+
'IMPORTANT: Images must be publicly accessible URLs. Use a hosting service or the data API to serve local images.',
|
|
94
|
+
'Rate limit: 25 posts per 24-hour period.',
|
|
95
|
+
'Requires a Business or Creator Instagram account linked to a Facebook Page.'
|
|
96
|
+
].join('\n')
|
|
97
|
+
}
|
|
98
|
+
];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type AuthStrategy = 'header' | 'bearer' | 'query' | 'oauth2';
|
|
2
|
+
|
|
3
|
+
export interface ConnectorField {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
type: 'password' | 'text';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ConnectorDefinition {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
category: string;
|
|
13
|
+
description: string;
|
|
14
|
+
baseUrl: string;
|
|
15
|
+
authStrategy: AuthStrategy;
|
|
16
|
+
authKey: string;
|
|
17
|
+
fields: ConnectorField[];
|
|
18
|
+
/** Usage hints shown to the LLM — recommended endpoints, default settings, gotchas. */
|
|
19
|
+
hints?: string;
|
|
20
|
+
/** OAuth2: Authorization endpoint URL. */
|
|
21
|
+
authorizationUrl?: string;
|
|
22
|
+
/** OAuth2: Token exchange endpoint URL. */
|
|
23
|
+
tokenUrl?: string;
|
|
24
|
+
/** OAuth2: Requested scopes. */
|
|
25
|
+
scopes?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ConnectorConfig {
|
|
29
|
+
apiKey: string;
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ConnectorOAuthConfig extends ConnectorConfig {
|
|
34
|
+
accessToken?: string;
|
|
35
|
+
refreshToken?: string;
|
|
36
|
+
expiresAt?: number;
|
|
37
|
+
userId?: string;
|
|
38
|
+
accountName?: string;
|
|
39
|
+
clientId?: string;
|
|
40
|
+
clientSecret?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type ConnectorsConfig = Record<string, ConnectorConfig | ConnectorOAuthConfig>;
|
|
44
|
+
|
|
45
|
+
export interface ConnectorSummary {
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
category: string;
|
|
49
|
+
configured: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ConnectorDetail extends ConnectorDefinition {
|
|
53
|
+
configured: boolean;
|
|
54
|
+
enabled: boolean;
|
|
55
|
+
hasKey: boolean;
|
|
56
|
+
connected?: boolean;
|
|
57
|
+
accountName?: string;
|
|
58
|
+
userId?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface ConnectorCallRequest {
|
|
62
|
+
connector: string;
|
|
63
|
+
method: string;
|
|
64
|
+
path: string;
|
|
65
|
+
headers?: Record<string, string>;
|
|
66
|
+
body?: unknown;
|
|
67
|
+
query?: Record<string, string>;
|
|
68
|
+
}
|
package/src/files.ts
CHANGED
|
@@ -36,6 +36,13 @@ export async function listFiles(path: string): Promise<string[]> {
|
|
|
36
36
|
return (await fs.readdir(path)).filter(file => !file.startsWith('.') && file.includes('.'));
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export async function listFolders(dirPath: string): Promise<string[]> {
|
|
40
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
41
|
+
return entries
|
|
42
|
+
.filter(entry => entry.isDirectory() && !entry.name.startsWith('.'))
|
|
43
|
+
.map(entry => entry.name);
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
export async function loadFile(path: string): Promise<string> {
|
|
40
47
|
return await fs.readFile(path, 'utf8');
|
|
41
48
|
}
|
|
@@ -47,3 +54,7 @@ export async function saveFile(path: string, content: string): Promise<void> {
|
|
|
47
54
|
export async function deleteFile(path: string): Promise<void> {
|
|
48
55
|
await fs.unlink(path);
|
|
49
56
|
}
|
|
57
|
+
|
|
58
|
+
export async function deleteFolder(dirPath: string): Promise<void> {
|
|
59
|
+
await fs.rm(dirPath, { recursive: true });
|
|
60
|
+
}
|