json-object-editor 0.10.610 → 0.10.623
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/CHANGELOG.md +8 -0
- package/_www/mcp-nav.js +1 -0
- package/app.js +26 -2
- package/css/joe-styles.css +1 -0
- package/css/joe.css +2 -1
- package/css/joe.min.css +1 -1
- package/js/JsonObjectEditor.jquery.craydent.js +27 -53
- package/js/joe-ai.js +399 -0
- package/js/joe.js +28 -54
- package/js/joe.min.js +1 -1
- package/package.json +2 -2
- package/readme.md +663 -526
- package/server/app-config.js +2 -1
- package/server/fields/core.js +35 -17
- package/server/modules/Server.js +50 -0
- package/server/modules/Storage.js +7 -1
- package/server/modules/Utils.js +2 -1
- package/server/plugins/awsConnect.js +32 -6
- package/server/plugins/chatgpt-assistants.js +3 -3
- package/server/plugins/chatgpt-responses.js +45 -0
- package/server/plugins/chatgpt.js +729 -0
- package/server/schemas/ai_assistant.js +255 -0
- package/server/schemas/ai_conversation.js +34 -0
- package/server/schemas/ai_prompt.js +323 -0
- package/server/schemas/ai_response.js +217 -0
- package/server/schemas/ai_tool.js +30 -0
- package/server/schemas/ai_widget_conversation.js +110 -0
- package/server/schemas/task.js +783 -778
- package/server/webconfig.js +3 -1
package/server/app-config.js
CHANGED
|
@@ -31,12 +31,13 @@ var apps = function(){
|
|
|
31
31
|
title:'JOE Platform',
|
|
32
32
|
info:'This is the master Data Model Management System (DMMS) admin page. It has access to all of your schemas and datasets.',
|
|
33
33
|
description:'JOE is the Json Object Editor. The Platform app has access to all your schemas and datasets.',
|
|
34
|
-
plugins:['auth.js','formBuilder.js','reportbuilder.js','callbackTester.js','memberRegistry.js','awsConnect.js','notifier.js','calendar.js','inventory.js','money.js','plugin-utils.js','chatgpt.js','chatgpt-assistants.js','chatgpt-tools.js'],
|
|
34
|
+
plugins:['auth.js','formBuilder.js','reportbuilder.js','callbackTester.js','memberRegistry.js','awsConnect.js','notifier.js','calendar.js','inventory.js','money.js','plugin-utils.js','chatgpt.js','chatgpt-assistants.js','chatgpt-tools.js','chatgpt-responses.js'],
|
|
35
35
|
collections:((default_schemas.concat(['schema','group',
|
|
36
36
|
'site','page','post','layout','block','include','event',
|
|
37
37
|
'project','board','task',
|
|
38
38
|
'transaction','budget','ledger',
|
|
39
39
|
'location','device','notification',
|
|
40
|
+
/*'ai_assistant','ai_conversation','ai_response','ai_tool','ai_prompt',*/
|
|
40
41
|
'visitor','question','form','submission','session','touchpoint','member']))).sort(),
|
|
41
42
|
dashboard:[
|
|
42
43
|
JOE.Apps.Cards.appHome({cssclass:'w3 h4'}),
|
package/server/fields/core.js
CHANGED
|
@@ -83,22 +83,7 @@ var fields = {
|
|
|
83
83
|
return item.instructions_format;
|
|
84
84
|
}
|
|
85
85
|
},
|
|
86
|
-
|
|
87
|
-
type: "select",
|
|
88
|
-
display: "Ai Model",
|
|
89
|
-
values: [
|
|
90
|
-
{ value: "gpt-4o", name: "GPT-4o (Fast, 128k)" },
|
|
91
|
-
{ value: "gpt-4.1", name: "GPT-4.1 (Strong, 1M)" },
|
|
92
|
-
{ value: "gpt-4.1-mini", name: "4.1-mini (Cheap, 1M)" },
|
|
93
|
-
{ value: "gpt-4.1-nano", name: "4.1-nano (Fastest, light tasks)" }
|
|
94
|
-
],
|
|
95
|
-
tooltip:`Ai Model Guide -
|
|
96
|
-
GPT-4o is the default for fast, responsive tasks and supports up to 128k tokens. It’s ideal for short completions, summaries, and dynamic UI tools.
|
|
97
|
-
GPT-4.1 and 4.1-mini support a massive 1 million token context, making them perfect for large inputs like full business profiles, long strategy texts, and multi-object analysis.
|
|
98
|
-
4.1-mini is significantly cheaper than full 4.1, with great balance for most structured AI workflows.
|
|
99
|
-
4.1-nano is best for lightweight classification or routing logic where speed and cost matter more than depth.`,
|
|
100
|
-
default: "gpt-4o",
|
|
101
|
-
},
|
|
86
|
+
|
|
102
87
|
template:{
|
|
103
88
|
height:'600px',
|
|
104
89
|
type:function(item){
|
|
@@ -300,6 +285,7 @@ var fields = {
|
|
|
300
285
|
autocomplete_template:'<joe-title>${name} (.${filetype})</joe-title><joe-subtext>${info}</joe-subtext>'
|
|
301
286
|
|
|
302
287
|
},
|
|
288
|
+
/* file_upload:{type:'uploader',allowmultiple:true, height:'300px',comment:'drag files here to upload', onConfirm:_joe.SERVER.Plugins.awsFileUpload,use_legacy:true},*/
|
|
303
289
|
datasets:{type:'group',cols:2,
|
|
304
290
|
comment:'which itemtypes(schemas) does this pertain to',values:function(){
|
|
305
291
|
if(typeof(__collectionNames) != undefined){
|
|
@@ -529,7 +515,39 @@ var fields = {
|
|
|
529
515
|
html += _joe.renderFieldListItem(item,temp,item.itemtype);
|
|
530
516
|
});
|
|
531
517
|
return html;
|
|
532
|
-
}}
|
|
518
|
+
}},
|
|
519
|
+
//AI Fields
|
|
520
|
+
ai_model:{
|
|
521
|
+
type: "select",
|
|
522
|
+
display: "Ai Model",
|
|
523
|
+
values: [
|
|
524
|
+
{ value:"gpt-5.1", name: "GPT-5.1 (Strong, 128k)" },
|
|
525
|
+
{ value:"gpt-5", name: "GPT-5 (Strong, 128K)" },
|
|
526
|
+
{ value:"gpt-5-mini", name: "GPT-5-mini (Cheap, 1M)" },
|
|
527
|
+
{ value:"gpt-5-nano", name: "GPT-5-nano (Fastest, light tasks)" },
|
|
528
|
+
{ value: "gpt-4o", name: "GPT-4o (Fast, 128k)" },
|
|
529
|
+
{ value: "gpt-4.1", name: "GPT-4.1 (Strong, 1M)" },
|
|
530
|
+
{ value: "gpt-4.1-mini", name: "4.1-mini (Cheap, 1M)" },
|
|
531
|
+
{ value: "gpt-4.1-nano", name: "4.1-nano (Fastest, light tasks)" }
|
|
532
|
+
],
|
|
533
|
+
tooltip:`Ai Model Guide -
|
|
534
|
+
GPT-4o is the default for fast, responsive tasks and supports up to 128k tokens. It’s ideal for short completions, summaries, and dynamic UI tools.
|
|
535
|
+
GPT-4.1 and 4.1-mini support a massive 1 million token context, making them perfect for large inputs like full business profiles, long strategy texts, and multi-object analysis.
|
|
536
|
+
4.1-mini is significantly cheaper than full 4.1, with great balance for most structured AI workflows.
|
|
537
|
+
4.1-nano is best for lightweight classification or routing logic where speed and cost matter more than depth.`,
|
|
538
|
+
default: "gpt-4o",
|
|
539
|
+
},
|
|
540
|
+
objectChat:{
|
|
541
|
+
type:'button',
|
|
542
|
+
display:'Start Chat',
|
|
543
|
+
icon:'ai_assistant',
|
|
544
|
+
onclick:function(object){
|
|
545
|
+
if (!object || !object._id) return '';
|
|
546
|
+
return `_joe.Ai.spawnChatHelper('${object._id}');`;
|
|
547
|
+
},
|
|
548
|
+
|
|
549
|
+
}
|
|
550
|
+
|
|
533
551
|
};
|
|
534
552
|
|
|
535
553
|
module.exports = fields;
|
package/server/modules/Server.js
CHANGED
|
@@ -169,6 +169,56 @@ server.get(JOE.webconfig.joepath+'_www/mcp-schemas.html',auth,function(req,res){
|
|
|
169
169
|
res.sendFile(path.join(JOE.joedir,'_www','mcp-schemas.html'));
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
+
// AI Widget test page (Responses + assistants) – auth protected
|
|
173
|
+
server.get(['/ai-widget-test.html', JOE.webconfig.joepath + 'ai-widget-test.html'], auth, function(req,res){
|
|
174
|
+
var assistantId = (req.query.assistant_id || req.query.assistant || '').trim();
|
|
175
|
+
if (!assistantId && JOE && JOE.Utils && JOE.Utils.Settings) {
|
|
176
|
+
try {
|
|
177
|
+
var settingObj = JOE.Utils.Settings('DEFAULT_AI_ASSISTANT', { object: true });
|
|
178
|
+
assistantId = (settingObj && settingObj.value) || '';
|
|
179
|
+
} catch(e) {
|
|
180
|
+
console.log('[ai-widget-test] error reading DEFAULT_AI_ASSISTANT:', e && e.message);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
var joePath = (JOE && JOE.webconfig && JOE.webconfig.joepath) || '/JsonObjectEditor/';
|
|
184
|
+
var assistantAttr = assistantId ? ' ai_assistant_id="'+assistantId.replace(/"/g,'"')+'"' : '';
|
|
185
|
+
|
|
186
|
+
res.send(`<!doctype html>
|
|
187
|
+
<html>
|
|
188
|
+
<head>
|
|
189
|
+
<meta charset="utf-8">
|
|
190
|
+
<title>JOE AI Widget Test</title>
|
|
191
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
192
|
+
<style>
|
|
193
|
+
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif;margin:20px;background:#f3f4f6;}
|
|
194
|
+
h1{margin-top:0;}
|
|
195
|
+
.small{font-size:13px;color:#6b7280;margin-bottom:16px;}
|
|
196
|
+
.container{max-width:480px;margin:0 auto;background:#fff;border-radius:12px;box-shadow:0 4px 14px rgba(0,0,0,0.06);padding:16px;}
|
|
197
|
+
.meta{font-size:12px;color:#6b7280;margin-bottom:8px;}
|
|
198
|
+
code{background:#e5e7eb;border-radius:4px;padding:2px 4px;font-size:12px;}
|
|
199
|
+
</style>
|
|
200
|
+
</head>
|
|
201
|
+
<body>
|
|
202
|
+
<div id="mcp-nav"></div>
|
|
203
|
+
<script src="${joePath}_www/mcp-nav.js"></script>
|
|
204
|
+
|
|
205
|
+
<div class="container">
|
|
206
|
+
<h1>AI Widget Test</h1>
|
|
207
|
+
<div class="small">
|
|
208
|
+
This page mounts <code><joe-ai-widget></code> and sends messages through
|
|
209
|
+
<code>/API/plugin/chatgpt/widget*</code> using the OpenAI Responses API.
|
|
210
|
+
${assistantId ? `Using <code>DEFAULT_AI_ASSISTANT</code> (ai_assistant_id=${assistantId}).` : 'No DEFAULT_AI_ASSISTANT is set; widget will use model defaults.'}
|
|
211
|
+
You can override the assistant via <code>?assistant_id=<ai_assistant _id></code>.
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<joe-ai-widget id="widget" title="JOE AI Assistant"${assistantAttr}></joe-ai-widget>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<script src="${joePath}js/joe-ai.js"></script>
|
|
218
|
+
</body>
|
|
219
|
+
</html>`);
|
|
220
|
+
});
|
|
221
|
+
|
|
172
222
|
server.use(JOE.webconfig.joepath,express.static(JOE.joedir));
|
|
173
223
|
|
|
174
224
|
//USER
|
|
@@ -101,7 +101,13 @@ function Storage(specs){
|
|
|
101
101
|
try{
|
|
102
102
|
if(!data.joeUpdated){ data.joeUpdated = new Date().toISOString(); }
|
|
103
103
|
if(!data.created){
|
|
104
|
-
var cachedBefore =
|
|
104
|
+
var cachedBefore = null;
|
|
105
|
+
// Only use Cache.findByID when _id is a string; ObjectId instances can cause internal split() errors.
|
|
106
|
+
if (data && data._id && typeof data._id === 'string' && JOE && JOE.Cache && JOE.Cache.findByID){
|
|
107
|
+
try {
|
|
108
|
+
cachedBefore = JOE.Cache.findByID(collection, data._id);
|
|
109
|
+
} catch(__e){ cachedBefore = null; }
|
|
110
|
+
}
|
|
105
111
|
if(!cachedBefore){ data.created = new Date().toISOString(); }
|
|
106
112
|
}
|
|
107
113
|
}catch(_e){}
|
package/server/modules/Utils.js
CHANGED
|
@@ -20,7 +20,8 @@ const s3 = new S3Client({
|
|
|
20
20
|
} : undefined
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
// Default to public-read unless explicitly overridden; allows legacy behavior to keep working
|
|
24
|
+
var ACL = (data.hasOwnProperty('ACL') ? data.ACL : 'public-read');
|
|
24
25
|
var Bucket = JOE.Cache.settings.AWS_BUCKETNAME;
|
|
25
26
|
var file = data.file || 'body';
|
|
26
27
|
// Create a bucket using bound parameters and put something in it.
|
|
@@ -35,7 +36,8 @@ if(data.base64){
|
|
|
35
36
|
var buf = Buffer.from(data.base64.replace(/^data:[a-zA-Z0-9\_\-]*\/[a-zA-Z0-9\_\-]*;base64,/, ""),'base64');
|
|
36
37
|
s3Params.Body = buf;
|
|
37
38
|
s3Params.ContentEncoding = 'base64';
|
|
38
|
-
|
|
39
|
+
// Prefer explicit contentType; fall back to extension or a safe default
|
|
40
|
+
s3Params.ContentType = data.contentType || data.extension || 'application/octet-stream';
|
|
39
41
|
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -51,17 +53,41 @@ var response = {
|
|
|
51
53
|
Key:Key,
|
|
52
54
|
bucket:Bucket
|
|
53
55
|
}
|
|
54
|
-
|
|
56
|
+
// Early validation
|
|
57
|
+
if(!Bucket){
|
|
58
|
+
return res.status(400).send({ error:'AWS bucket is not configured (settings.AWS_BUCKETNAME).' });
|
|
59
|
+
}
|
|
60
|
+
if(!config || !config.region){
|
|
61
|
+
return res.status(400).send({ error:'AWS S3 region is missing. Set settings.AWS_S3CONFIG.region.' });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Only include ACL if requested (string and not empty)
|
|
65
|
+
if(ACL){
|
|
66
|
+
s3Params.ACL = ACL;
|
|
67
|
+
}
|
|
68
|
+
|
|
55
69
|
s3.send(new PutObjectCommand(s3Params))
|
|
56
70
|
.then(function(data){
|
|
71
|
+
// Construct canonical URL from region + bucket
|
|
72
|
+
var region = config.region;
|
|
73
|
+
var url = 'https://'+Bucket+'.s3.'+region+'.amazonaws.com/'+Key;
|
|
57
74
|
response.data = data;
|
|
58
|
-
|
|
75
|
+
response.url = url;
|
|
76
|
+
res.status(200).send(response);
|
|
59
77
|
console.log("Successfully uploaded data to "+Key);
|
|
60
78
|
})
|
|
61
79
|
.catch(function(err){
|
|
62
|
-
|
|
63
|
-
|
|
80
|
+
var code = (err && (err.Code || err.name)) || 'UploadError';
|
|
81
|
+
var message = (err && (err.message || String(err))) || 'Upload failed';
|
|
82
|
+
// Clear, actionable error when ACLs are disabled on bucket
|
|
83
|
+
if(code === 'AccessControlListNotSupported'){
|
|
84
|
+
return res.status(400).send({
|
|
85
|
+
error: 'Bucket has ACLs disabled (Object Ownership enforced). Remove ACL or switch to presigned/proxy access.',
|
|
86
|
+
code: code
|
|
87
|
+
});
|
|
88
|
+
}
|
|
64
89
|
console.log("Error uploading data: ", err);
|
|
90
|
+
res.status(500).send({ error: message, code: code });
|
|
65
91
|
});
|
|
66
92
|
return {use_callback:true};
|
|
67
93
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const savedArrayDelete = Array.prototype.delete;
|
|
2
|
+
delete Array.prototype.delete;
|
|
3
3
|
const OpenAI = require("openai");
|
|
4
|
+
if (savedArrayDelete) Array.prototype.delete = savedArrayDelete;
|
|
4
5
|
|
|
5
6
|
function ChatGPTAssistants() {
|
|
6
7
|
const self = this;
|
|
@@ -67,7 +68,6 @@ function ChatGPTAssistants() {
|
|
|
67
68
|
}
|
|
68
69
|
this.syncAssistantToOpenAI = async function(data, req, res) {
|
|
69
70
|
const openai = newClient();
|
|
70
|
-
|
|
71
71
|
try {
|
|
72
72
|
coloredLog("🔄 Starting syncAssistantToOpenAI");
|
|
73
73
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const OpenAI = require("openai");
|
|
2
|
+
|
|
3
|
+
function ChatGPTResponses() {
|
|
4
|
+
const self = this;
|
|
5
|
+
|
|
6
|
+
function log(prefix, message, data) {
|
|
7
|
+
const p = JOE.Utils.color('[chatgpt-responses]', 'plugin', false);
|
|
8
|
+
console.log(p, prefix, message || '', (data !== undefined ? data : ''));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.default = function (data, req, res) {
|
|
12
|
+
try {
|
|
13
|
+
return {
|
|
14
|
+
success: true,
|
|
15
|
+
message: 'chatgpt-responses proxy plugin ready',
|
|
16
|
+
params: req.params,
|
|
17
|
+
query: req.query,
|
|
18
|
+
body: req.body
|
|
19
|
+
};
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return { errors: 'plugin error: ' + e, failedat: 'plugin' };
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Thin proxy to chatgpt.autofill to avoid duplication
|
|
26
|
+
this.autofill = async function (data, req, res) {
|
|
27
|
+
try {
|
|
28
|
+
const base = JOE.Apps && JOE.Apps.plugins && JOE.Apps.plugins['chatgpt'];
|
|
29
|
+
if (!base || !base.autofill) {
|
|
30
|
+
return { success: false, error: 'chatgpt.autofill not available' };
|
|
31
|
+
}
|
|
32
|
+
const result = await base.autofill(data, req, res);
|
|
33
|
+
return result;
|
|
34
|
+
} catch (e) {
|
|
35
|
+
log('error', e && e.message);
|
|
36
|
+
return { success: false, error: e && e.message || 'Unknown error' };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return self;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = new ChatGPTResponses();
|
|
44
|
+
|
|
45
|
+
|