iobroker.javascript 9.2.0 → 9.2.2
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 +23 -13
- package/admin/assets/AiChatPanel-Bbux7LP7.js +315 -0
- package/admin/assets/AiInlineProvider-DkgjVSFQ.js +7 -0
- package/admin/assets/ScriptEditor-DKljeYQN.js +2 -0
- package/admin/assets/ScriptEditorVanillaMonaco-CaFyKuJ2.js +3 -0
- package/admin/assets/applyCodeEdit-BdRnbz2g.js +1 -0
- package/admin/assets/index-CexrwGGC.js +986 -0
- package/admin/assets/{index-DW5CwIma.js → index-DSvAO584.js} +132 -121
- package/admin/assets/{index-BI3JsAz_.js → index-DgE6XhUM.js} +1 -1
- package/admin/assets/{index-DAkyON1r.js → index-_BUuZlI0.js} +1 -1
- package/admin/assets/inlineChatWidget-Cu6rb4Ar.js +2 -0
- package/admin/assets/inlineDiffController-DBLpPOe2.js +16 -0
- package/admin/assets/{localSharedImportMap-CqvxvyGG.js → localSharedImportMap-BCwqnmn4.js} +1 -1
- package/admin/assets/stateHoverProvider-CGChzV5W.js +3 -0
- package/admin/assets/{virtual_mf-REMOTE_ENTRY_ID_iobroker_javascript__remoteEntry_js-BJ03G6Pu.js → virtual_mf-REMOTE_ENTRY_ID_iobroker_javascript__remoteEntry_js-DNrEsMrg.js} +2 -2
- package/admin/custom/assets/{index-BVwMgafs.js → index-DNKzDki_.js} +152 -152
- package/admin/custom/assets/{index-DsIn9jH_.js → index-NItS-TWp.js} +1 -1
- package/admin/{assets/index-hwplVCgv.js → custom/assets/index-sozs10Ij.js} +45 -71
- package/admin/custom/assets/{localSharedImportMap-C4qXtGUd.js → localSharedImportMap-BxT0VwTO.js} +1 -1
- package/admin/custom/assets/{virtual_mf-REMOTE_ENTRY_ID_ConfigCustomJavascriptSet__customComponents_js-DFSXB0v2.js → virtual_mf-REMOTE_ENTRY_ID_ConfigCustomJavascriptSet__customComponents_js-Br3IlnqO.js} +2 -2
- package/admin/custom/customComponents.js +1 -1
- package/admin/google-blockly/own/blocks_system.js +34 -0
- package/admin/google-blockly/own/blocks_words.js +4 -0
- package/admin/jsonConfig.json +9 -9
- package/admin/mf-manifest.json +1 -1
- package/admin/remoteEntry.js +1 -1
- package/admin/tab.html +2 -2
- package/build/lib/aiProviderResolver.js +80 -0
- package/build/lib/aiProviderResolver.js.map +1 -0
- package/build/lib/anthropicAdapter.js +175 -0
- package/build/lib/anthropicAdapter.js.map +1 -0
- package/build/lib/sandbox.js +7 -2
- package/build/lib/sandbox.js.map +1 -1
- package/build/main.js +141 -107
- package/build/main.js.map +1 -1
- package/build/types.d.ts +1 -0
- package/docs/de/README.md +64 -4
- package/docs/de/blockly.md +669 -8
- package/docs/en/README.md +62 -4
- package/docs/en/blockly.md +33 -0
- package/io-package.json +81 -1
- package/package.json +1 -1
- package/admin/assets/AiChatPanel-sgt697A2.js +0 -296
- package/admin/assets/AiInlineProvider-Dd7eKxk4.js +0 -7
- package/admin/assets/ScriptEditor-CH4qJzqq.js +0 -2
- package/admin/assets/ScriptEditorVanillaMonaco-cB_90T0P.js +0 -1
- package/admin/custom/assets/index-CVIpIckw.js +0 -960
package/build/main.js
CHANGED
|
@@ -78,6 +78,8 @@ const protectFs_1 = __importDefault(require("./lib/protectFs"));
|
|
|
78
78
|
const words_1 = require("./lib/words");
|
|
79
79
|
const sandbox_1 = require("./lib/sandbox");
|
|
80
80
|
const nodeModulesManagement_1 = require("./lib/nodeModulesManagement");
|
|
81
|
+
const aiProviderResolver_1 = require("./lib/aiProviderResolver");
|
|
82
|
+
const anthropicAdapter_1 = require("./lib/anthropicAdapter");
|
|
81
83
|
const eventObj_1 = require("./lib/eventObj");
|
|
82
84
|
const scheduler_1 = require("./lib/scheduler");
|
|
83
85
|
const typescriptSettings_1 = require("./lib/typescriptSettings");
|
|
@@ -918,14 +920,15 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
918
920
|
break;
|
|
919
921
|
}
|
|
920
922
|
case 'chatCompletion': {
|
|
921
|
-
// Proxy chat completion requests to an OpenAI-compatible API endpoint
|
|
923
|
+
// Proxy chat completion requests to an OpenAI-compatible API endpoint.
|
|
924
|
+
// API keys are resolved server-side from encryptedNative config — they never
|
|
925
|
+
// leave the adapter (frontend only sends `provider`, not the key).
|
|
922
926
|
if (obj.callback) {
|
|
923
|
-
const baseUrl = (obj.message?.baseUrl || '').trim();
|
|
924
|
-
const apiKey = (obj.message?.apiKey || '').trim();
|
|
925
927
|
const chatModel = (obj.message?.model || '').trim();
|
|
926
928
|
const messages = obj.message?.messages;
|
|
927
929
|
const tools = obj.message?.tools;
|
|
928
930
|
const provider = (obj.message?.provider || 'openai').trim();
|
|
931
|
+
const { apiKey, baseUrl } = (0, aiProviderResolver_1.resolveProviderCredentials)(this.config, provider, obj.message?.baseUrl);
|
|
929
932
|
// Anthropic, Gemini, and DeepSeek always require an API key; OpenAI-compatible allows empty key with custom base URL
|
|
930
933
|
if (!apiKey &&
|
|
931
934
|
(provider === 'anthropic' || provider === 'gemini' || provider === 'deepseek' || !baseUrl)) {
|
|
@@ -945,15 +948,16 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
945
948
|
url = 'https://api.anthropic.com/v1/messages';
|
|
946
949
|
chatHeaders['x-api-key'] = apiKey;
|
|
947
950
|
chatHeaders['anthropic-version'] = '2023-06-01';
|
|
948
|
-
|
|
949
|
-
const
|
|
950
|
-
const
|
|
951
|
+
// Translate OpenAI-format messages/tools into Anthropic's content-block format.
|
|
952
|
+
const { system: systemText, messages: anthropicMessages } = (0, anthropicAdapter_1.translateMessagesToAnthropic)(messages);
|
|
953
|
+
const anthropicTools = tools?.length ? (0, anthropicAdapter_1.translateToolsToAnthropic)(tools) : [];
|
|
951
954
|
bodyObj = {
|
|
952
955
|
model: chatModel,
|
|
953
956
|
max_tokens: 8192,
|
|
954
957
|
stream: false,
|
|
955
958
|
...(systemText ? { system: systemText } : {}),
|
|
956
|
-
messages:
|
|
959
|
+
messages: anthropicMessages,
|
|
960
|
+
...(anthropicTools.length ? { tools: anthropicTools } : {}),
|
|
957
961
|
};
|
|
958
962
|
}
|
|
959
963
|
else if (provider === 'gemini') {
|
|
@@ -995,69 +999,86 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
995
999
|
}
|
|
996
1000
|
const isHttps = urlObj.protocol === 'https:';
|
|
997
1001
|
const requestModule = isHttps ? https : http;
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
data
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1002
|
+
try {
|
|
1003
|
+
const req = requestModule.request(url, {
|
|
1004
|
+
method: 'POST',
|
|
1005
|
+
headers: chatHeaders,
|
|
1006
|
+
timeout: 600000,
|
|
1007
|
+
...(isHttps && this.config.allowSelfSignedCerts ? { rejectUnauthorized: false } : {}),
|
|
1008
|
+
}, res => {
|
|
1009
|
+
let data = '';
|
|
1010
|
+
res.on('data', (chunk) => {
|
|
1011
|
+
data += chunk.toString();
|
|
1012
|
+
});
|
|
1013
|
+
res.on('end', () => {
|
|
1014
|
+
if (res.statusCode === 200) {
|
|
1015
|
+
try {
|
|
1016
|
+
const parsed = JSON.parse(data);
|
|
1017
|
+
let content;
|
|
1018
|
+
let tool_calls;
|
|
1019
|
+
if (provider === 'anthropic') {
|
|
1020
|
+
const translated = (0, anthropicAdapter_1.translateAnthropicResponseToOpenAI)(parsed);
|
|
1021
|
+
content = translated.content;
|
|
1022
|
+
tool_calls = translated.tool_calls;
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
const message = parsed.choices?.[0]?.message;
|
|
1026
|
+
content = message?.content || '';
|
|
1027
|
+
tool_calls = message?.tool_calls;
|
|
1028
|
+
}
|
|
1029
|
+
if (!content && !tool_calls?.length) {
|
|
1030
|
+
this.sendTo(obj.from, obj.command, { error: 'Empty response from API' }, obj.callback);
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
this.sendTo(obj.from, obj.command, {
|
|
1034
|
+
success: true,
|
|
1035
|
+
content,
|
|
1036
|
+
...(tool_calls ? { tool_calls } : {}),
|
|
1037
|
+
}, obj.callback);
|
|
1038
|
+
}
|
|
1019
1039
|
}
|
|
1020
|
-
|
|
1021
|
-
this.sendTo(obj.from, obj.command, {
|
|
1040
|
+
catch {
|
|
1041
|
+
this.sendTo(obj.from, obj.command, { error: 'Invalid JSON response from API' }, obj.callback);
|
|
1022
1042
|
}
|
|
1023
1043
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1044
|
+
else {
|
|
1045
|
+
let detail = '';
|
|
1046
|
+
try {
|
|
1047
|
+
const errParsed = JSON.parse(data);
|
|
1048
|
+
detail = errParsed.error?.message || data.substring(0, 200);
|
|
1049
|
+
}
|
|
1050
|
+
catch {
|
|
1051
|
+
detail = data.substring(0, 200);
|
|
1052
|
+
}
|
|
1053
|
+
this.sendTo(obj.from, obj.command, {
|
|
1054
|
+
error: `${detail || httpStatusText(res.statusCode || 0)} (${res.statusCode})`,
|
|
1055
|
+
}, obj.callback);
|
|
1036
1056
|
}
|
|
1037
|
-
|
|
1038
|
-
error: `${detail || httpStatusText(res.statusCode || 0)} (${res.statusCode})`,
|
|
1039
|
-
}, obj.callback);
|
|
1040
|
-
}
|
|
1057
|
+
});
|
|
1041
1058
|
});
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1059
|
+
req.on('error', (err) => {
|
|
1060
|
+
this.sendTo(obj.from, obj.command, { error: `Connection failed: ${err.message}` }, obj.callback);
|
|
1061
|
+
});
|
|
1062
|
+
req.on('timeout', () => {
|
|
1063
|
+
req.destroy();
|
|
1064
|
+
this.sendTo(obj.from, obj.command, { error: 'Connection timeout (600s)' }, obj.callback);
|
|
1065
|
+
});
|
|
1066
|
+
req.write(bodyBuffer);
|
|
1067
|
+
req.end();
|
|
1068
|
+
}
|
|
1069
|
+
catch (error) {
|
|
1070
|
+
this.sendTo(obj.from, obj.command, { error: `Connection failed: ${error.toString()}` }, obj.callback);
|
|
1071
|
+
}
|
|
1052
1072
|
}
|
|
1053
1073
|
break;
|
|
1054
1074
|
}
|
|
1055
1075
|
case 'testApiConnection': {
|
|
1056
|
-
// Test connection to an OpenAI-compatible API endpoint
|
|
1076
|
+
// Test connection to an OpenAI-compatible API endpoint.
|
|
1077
|
+
// The settings-dialog Test button sends the current form value as `apiKey`
|
|
1078
|
+
// (so users can test before saving); otherwise we fall back to the stored key.
|
|
1057
1079
|
if (obj.callback) {
|
|
1058
|
-
const baseUrl = (obj.message?.baseUrl || '').trim();
|
|
1059
|
-
const apiKey = (obj.message?.apiKey || '').trim();
|
|
1060
1080
|
const provider = (obj.message?.provider || 'openai').trim();
|
|
1081
|
+
const { apiKey, baseUrl } = (0, aiProviderResolver_1.resolveTestCredentials)(this.config, provider, obj.message?.apiKey, obj.message?.baseUrl);
|
|
1061
1082
|
// Anthropic, Gemini, and DeepSeek always require an API key; OpenAI-compatible allows empty key with custom base URL
|
|
1062
1083
|
if (!apiKey &&
|
|
1063
1084
|
(provider === 'anthropic' || provider === 'gemini' || provider === 'deepseek' || !baseUrl)) {
|
|
@@ -1099,59 +1120,72 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1099
1120
|
}
|
|
1100
1121
|
const isHttps = urlObj.protocol === 'https:';
|
|
1101
1122
|
const requestModule = isHttps ? https : http;
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
data
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1123
|
+
try {
|
|
1124
|
+
const req = requestModule.request(url, {
|
|
1125
|
+
method: 'GET',
|
|
1126
|
+
headers: testHeaders,
|
|
1127
|
+
timeout: 10000,
|
|
1128
|
+
...(isHttps && this.config.allowSelfSignedCerts ? { rejectUnauthorized: false } : {}),
|
|
1129
|
+
}, res => {
|
|
1130
|
+
let data = '';
|
|
1131
|
+
res.on('data', (chunk) => {
|
|
1132
|
+
data += chunk.toString();
|
|
1133
|
+
});
|
|
1134
|
+
res.on('end', () => {
|
|
1135
|
+
if (res.statusCode === 200) {
|
|
1136
|
+
try {
|
|
1137
|
+
const parsed = JSON.parse(data);
|
|
1138
|
+
const models = (parsed.data || [])
|
|
1139
|
+
.map((m) => m.id.startsWith('models/') ? m.id.substring(7) : m.id)
|
|
1140
|
+
.sort();
|
|
1141
|
+
this.sendTo(obj.from, obj.command, { success: true, models, count: models.length }, obj.callback);
|
|
1142
|
+
}
|
|
1143
|
+
catch {
|
|
1144
|
+
this.sendTo(obj.from, obj.command, { error: 'Invalid JSON response from API' }, obj.callback);
|
|
1145
|
+
}
|
|
1120
1146
|
}
|
|
1121
|
-
|
|
1122
|
-
this.sendTo(obj.from, obj.command, { error: 'Invalid
|
|
1147
|
+
else if (res.statusCode === 401) {
|
|
1148
|
+
this.sendTo(obj.from, obj.command, { error: 'Invalid API key (401)' }, obj.callback);
|
|
1123
1149
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
this.sendTo(obj.from, obj.command, { error: 'Invalid API key (401)' }, obj.callback);
|
|
1127
|
-
}
|
|
1128
|
-
else if (res.statusCode === 403) {
|
|
1129
|
-
this.sendTo(obj.from, obj.command, { error: 'Access denied (403)' }, obj.callback);
|
|
1130
|
-
}
|
|
1131
|
-
else {
|
|
1132
|
-
// Include response body for debugging
|
|
1133
|
-
let detail = '';
|
|
1134
|
-
try {
|
|
1135
|
-
const errParsed = JSON.parse(data);
|
|
1136
|
-
detail = errParsed.error?.message || data.substring(0, 200);
|
|
1150
|
+
else if (res.statusCode === 403) {
|
|
1151
|
+
this.sendTo(obj.from, obj.command, { error: 'Access denied (403)' }, obj.callback);
|
|
1137
1152
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1153
|
+
else {
|
|
1154
|
+
// Include response body for debugging
|
|
1155
|
+
let detail = '';
|
|
1156
|
+
try {
|
|
1157
|
+
const errParsed = JSON.parse(data);
|
|
1158
|
+
detail = errParsed.error?.message || data.substring(0, 200);
|
|
1159
|
+
}
|
|
1160
|
+
catch {
|
|
1161
|
+
detail = data.substring(0, 200);
|
|
1162
|
+
}
|
|
1163
|
+
this.sendTo(obj.from, obj.command, {
|
|
1164
|
+
error: `${detail || httpStatusText(res.statusCode || 0)} (${res.statusCode})`,
|
|
1165
|
+
}, obj.callback);
|
|
1140
1166
|
}
|
|
1141
|
-
|
|
1142
|
-
error: `${detail || httpStatusText(res.statusCode || 0)} (${res.statusCode})`,
|
|
1143
|
-
}, obj.callback);
|
|
1144
|
-
}
|
|
1167
|
+
});
|
|
1145
1168
|
});
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1169
|
+
req.on('error', (err) => {
|
|
1170
|
+
this.sendTo(obj.from, obj.command, { error: `Connection failed: ${err.message}` }, obj.callback);
|
|
1171
|
+
});
|
|
1172
|
+
req.on('timeout', () => {
|
|
1173
|
+
req.destroy();
|
|
1174
|
+
this.sendTo(obj.from, obj.command, { error: 'Connection timeout (10s)' }, obj.callback);
|
|
1175
|
+
});
|
|
1176
|
+
req.end();
|
|
1177
|
+
}
|
|
1178
|
+
catch (error) {
|
|
1179
|
+
this.sendTo(obj.from, obj.command, { error: `Connection failed: ${error.toString()}` }, obj.callback);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
case 'getAvailableAiProviders': {
|
|
1185
|
+
// Reports which AI providers have stored credentials (keys never leave the backend).
|
|
1186
|
+
if (obj.callback) {
|
|
1187
|
+
const providers = (0, aiProviderResolver_1.listAvailableProviders)(this.config);
|
|
1188
|
+
this.sendTo(obj.from, obj.command, { providers }, obj.callback);
|
|
1155
1189
|
}
|
|
1156
1190
|
break;
|
|
1157
1191
|
}
|