iobroker.javascript 9.1.1 → 9.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -16
- package/admin/assets/AiChatPanel-Cc1KscBJ.js +315 -0
- package/admin/assets/AiDatapointProvider-B0i3I5Fm.js +2 -0
- package/admin/assets/AiDiffView-RPozjjHf.js +1 -0
- package/admin/assets/AiInlineProvider-DkgjVSFQ.js +7 -0
- package/admin/assets/{Save-Cld9qv9i.js → Save-CkuNSiWL.js} +1 -1
- package/admin/assets/ScriptEditor-6qrtvwPL.js +2 -0
- package/admin/assets/ScriptEditorVanillaMonaco-bJua4sm1.js +3 -0
- package/admin/assets/applyCodeEdit-BdRnbz2g.js +1 -0
- package/admin/assets/{createSvgIcon-DPvF8xqu.js → createSvgIcon-DYs_tNrE.js} +1 -1
- package/admin/assets/docs-compact-CKNGiYh2.js +336 -0
- package/admin/assets/{index-BVPl8hv0.js → index-BZxCaQhO.js} +5 -5
- package/admin/assets/index-CYbVtccs.js +1267 -0
- package/admin/assets/index-CexrwGGC.js +986 -0
- package/admin/assets/{index-DFZipply.js → index-DsW7vBcg.js} +1 -1
- package/admin/assets/index-SHs7_wDh.js +1 -0
- package/admin/assets/{index-DlDUIfpA.js → index-dN73sbyQ.js} +1 -1
- package/admin/assets/index-wHNMOf-c.js +5 -0
- package/admin/assets/inheritsLoose-CIXm_zPk.js +29 -0
- package/admin/assets/inlineChatWidget-Cu6rb4Ar.js +2 -0
- package/admin/assets/inlineDiffController-DBLpPOe2.js +16 -0
- package/admin/assets/{iobroker_javascript__loadShare___mf_0_mui_mf_1_material__loadShare__.mjs-QWG5Arqe.js → iobroker_javascript__loadShare___mf_0_mui_mf_1_material__loadShare__.mjs-CV1_X-Lf.js} +1 -1
- package/admin/assets/{localSharedImportMap-xmSu9Qkg.js → localSharedImportMap-BCwqnmn4.js} +1 -1
- package/admin/assets/stateHoverProvider-CGChzV5W.js +3 -0
- package/admin/assets/useTheme-DuTJsNQ-.js +1 -0
- package/admin/assets/{virtual_mf-REMOTE_ENTRY_ID_iobroker_javascript__remoteEntry_js-D-eF6r8M.js → virtual_mf-REMOTE_ENTRY_ID_iobroker_javascript__remoteEntry_js-DNrEsMrg.js} +2 -2
- package/admin/custom/assets/{Components-B8x6wOMD.js → Components-B9ZyjeRI.js} +1 -1
- package/admin/custom/assets/{index-N2n19KrF.js → index-DNKzDki_.js} +152 -152
- package/admin/custom/assets/{index-DbXK0odl.js → index-NItS-TWp.js} +1 -1
- package/admin/custom/assets/index-sozs10Ij.js +960 -0
- package/admin/custom/assets/{localSharedImportMap-2Enh8EZe.js → localSharedImportMap-BxT0VwTO.js} +1 -1
- package/admin/custom/assets/{virtualExposes-CqrW7o8v.js → virtualExposes-jSvr-kK3.js} +1 -1
- package/admin/custom/assets/{virtual_mf-REMOTE_ENTRY_ID_ConfigCustomJavascriptSet__customComponents_js-BZZsMeVW.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_words.js +7 -0
- package/admin/google-blockly/own/field_oid.js +311 -37
- package/admin/i18n/de.json +8 -0
- package/admin/i18n/en.json +8 -0
- package/admin/i18n/es.json +8 -0
- package/admin/i18n/flat.txt +427 -0
- package/admin/i18n/fr.json +8 -0
- package/admin/i18n/it.json +8 -0
- package/admin/i18n/nl.json +8 -0
- package/admin/i18n/pl.json +8 -0
- package/admin/i18n/pt.json +8 -0
- package/admin/i18n/ru.json +8 -0
- package/admin/i18n/uk.json +8 -0
- package/admin/i18n/zh-cn.json +8 -0
- package/admin/img/anthropic.svg +1 -0
- package/admin/img/custom.svg +1 -0
- package/admin/img/deepseek.svg +1 -0
- package/admin/img/gemini.svg +1 -0
- package/admin/img/openai.svg +1 -0
- package/admin/jsonConfig.json +80 -18
- package/admin/mf-manifest.json +1 -1
- package/admin/remoteEntry.js +1 -1
- package/admin/tab.html +6 -6
- 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/debugger.js +2 -2
- package/build/lib/debugger.js.map +1 -1
- package/build/lib/inspect.js.map +1 -1
- package/build/lib/nodeModulesManagement.js +2 -2
- package/build/lib/nodeModulesManagement.js.map +1 -1
- package/build/lib/protectFs.js +5 -0
- package/build/lib/protectFs.js.map +1 -1
- package/build/lib/sandbox.js +9 -4
- package/build/lib/sandbox.js.map +1 -1
- package/build/main.js +190 -101
- package/build/main.js.map +1 -1
- package/build/types.d.ts +3 -0
- package/docs/de/README.md +81 -10
- package/docs/de/blockly.md +34 -0
- package/docs/en/README.md +81 -10
- package/docs/en/blockly.md +34 -0
- package/install/installTypings.js +1 -1
- package/io-package.json +82 -54
- package/package.json +7 -6
- package/admin/assets/OpenAiDialog-rhLuRLIM.js +0 -13
- package/admin/assets/ScriptEditor-C-fO5h68.js +0 -2
- package/admin/assets/ScriptEditorVanillaMonaco-DSYvOslZ.js +0 -1
- package/admin/assets/docs-BrInh2Pn.js +0 -2036
- package/admin/assets/index-C49cpVll.js +0 -5
- package/admin/assets/index-XSrHkgLE.js +0 -1230
- package/admin/assets/index-gIA8x-8f.js +0 -1
- package/admin/assets/index-q6iFaoFb.js +0 -986
- package/admin/assets/inheritsLoose-B0Ba5G9z.js +0 -29
- package/admin/assets/useTheme-hy6f3gra.js +0 -1
- package/admin/custom/assets/index-CYc31Gwg.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");
|
|
@@ -225,6 +227,19 @@ const jsDeclarationServer = new virtual_tsc_1.Server(typescriptSettings_1.jsDecl
|
|
|
225
227
|
* Stores the IDs of script objects whose change should be ignored because
|
|
226
228
|
* the compiled source was just updated
|
|
227
229
|
*/
|
|
230
|
+
function httpStatusText(code) {
|
|
231
|
+
const texts = {
|
|
232
|
+
400: 'Bad Request',
|
|
233
|
+
401: 'Unauthorized',
|
|
234
|
+
403: 'Forbidden',
|
|
235
|
+
404: 'Not Found',
|
|
236
|
+
429: 'Too Many Requests / Rate Limit',
|
|
237
|
+
500: 'Internal Server Error',
|
|
238
|
+
502: 'Bad Gateway',
|
|
239
|
+
503: 'Service Unavailable',
|
|
240
|
+
};
|
|
241
|
+
return texts[code] || `Error ${code}`;
|
|
242
|
+
}
|
|
228
243
|
class JavaScript extends adapter_core_1.Adapter {
|
|
229
244
|
context;
|
|
230
245
|
errorLogFunction = {
|
|
@@ -565,9 +580,9 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
565
580
|
await this.loadScriptById(id);
|
|
566
581
|
}
|
|
567
582
|
}
|
|
568
|
-
else {
|
|
569
|
-
|
|
570
|
-
// Source changed => restart the script
|
|
583
|
+
else if (obj.common.engine === `system.adapter.${this.namespace}` ||
|
|
584
|
+
formerObj.common.engine === `system.adapter.${this.namespace}`) {
|
|
585
|
+
// Source changed => restart the script (only on the relevant instance)
|
|
571
586
|
this.stopCounters[id] = this.stopCounters[id] ? this.stopCounters[id] + 1 : 1;
|
|
572
587
|
void this.stopScript(id).then(() => {
|
|
573
588
|
// only start again after stop when "last" object change to prevent problems on
|
|
@@ -905,13 +920,15 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
905
920
|
break;
|
|
906
921
|
}
|
|
907
922
|
case 'chatCompletion': {
|
|
908
|
-
// 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).
|
|
909
926
|
if (obj.callback) {
|
|
910
|
-
const baseUrl = (obj.message?.baseUrl || '').trim();
|
|
911
|
-
const apiKey = (obj.message?.apiKey || '').trim();
|
|
912
927
|
const chatModel = (obj.message?.model || '').trim();
|
|
913
928
|
const messages = obj.message?.messages;
|
|
929
|
+
const tools = obj.message?.tools;
|
|
914
930
|
const provider = (obj.message?.provider || 'openai').trim();
|
|
931
|
+
const { apiKey, baseUrl } = (0, aiProviderResolver_1.resolveProviderCredentials)(this.config, provider, obj.message?.baseUrl);
|
|
915
932
|
// Anthropic, Gemini, and DeepSeek always require an API key; OpenAI-compatible allows empty key with custom base URL
|
|
916
933
|
if (!apiKey &&
|
|
917
934
|
(provider === 'anthropic' || provider === 'gemini' || provider === 'deepseek' || !baseUrl)) {
|
|
@@ -931,15 +948,16 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
931
948
|
url = 'https://api.anthropic.com/v1/messages';
|
|
932
949
|
chatHeaders['x-api-key'] = apiKey;
|
|
933
950
|
chatHeaders['anthropic-version'] = '2023-06-01';
|
|
934
|
-
|
|
935
|
-
const
|
|
936
|
-
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) : [];
|
|
937
954
|
bodyObj = {
|
|
938
955
|
model: chatModel,
|
|
939
956
|
max_tokens: 8192,
|
|
940
957
|
stream: false,
|
|
941
958
|
...(systemText ? { system: systemText } : {}),
|
|
942
|
-
messages:
|
|
959
|
+
messages: anthropicMessages,
|
|
960
|
+
...(anthropicTools.length ? { tools: anthropicTools } : {}),
|
|
943
961
|
};
|
|
944
962
|
}
|
|
945
963
|
else if (provider === 'gemini') {
|
|
@@ -947,19 +965,26 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
947
965
|
if (apiKey) {
|
|
948
966
|
chatHeaders.Authorization = `Bearer ${apiKey}`;
|
|
949
967
|
}
|
|
950
|
-
bodyObj = { model: chatModel, messages, stream: false };
|
|
968
|
+
bodyObj = { model: chatModel, messages, stream: false, ...(tools?.length ? { tools } : {}) };
|
|
951
969
|
}
|
|
952
970
|
else if (provider === 'deepseek') {
|
|
953
971
|
url = 'https://api.deepseek.com/chat/completions';
|
|
954
972
|
chatHeaders.Authorization = `Bearer ${apiKey}`;
|
|
955
|
-
bodyObj = { model: chatModel, messages, stream: false };
|
|
973
|
+
bodyObj = { model: chatModel, messages, stream: false, ...(tools?.length ? { tools } : {}) };
|
|
956
974
|
}
|
|
957
975
|
else {
|
|
958
976
|
url = `${baseUrl || 'https://api.openai.com/v1'}/chat/completions`;
|
|
959
977
|
if (apiKey) {
|
|
960
978
|
chatHeaders.Authorization = `Bearer ${apiKey}`;
|
|
961
979
|
}
|
|
962
|
-
bodyObj = {
|
|
980
|
+
bodyObj = {
|
|
981
|
+
model: chatModel,
|
|
982
|
+
messages,
|
|
983
|
+
stream: false,
|
|
984
|
+
...(tools?.length ? { tools } : {}),
|
|
985
|
+
// Disable thinking/reasoning for local models to save context and speed
|
|
986
|
+
...(baseUrl ? { reasoning_effort: 'none' } : {}),
|
|
987
|
+
};
|
|
963
988
|
}
|
|
964
989
|
const body = JSON.stringify(bodyObj);
|
|
965
990
|
const bodyBuffer = Buffer.from(body, 'utf8');
|
|
@@ -974,67 +999,86 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
974
999
|
}
|
|
975
1000
|
const isHttps = urlObj.protocol === 'https:';
|
|
976
1001
|
const requestModule = isHttps ? https : http;
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
data
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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
|
+
}
|
|
996
1039
|
}
|
|
997
|
-
|
|
998
|
-
this.sendTo(obj.from, obj.command, {
|
|
1040
|
+
catch {
|
|
1041
|
+
this.sendTo(obj.from, obj.command, { error: 'Invalid JSON response from API' }, obj.callback);
|
|
999
1042
|
}
|
|
1000
1043
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
let errorMsg = `API returned status ${res.statusCode}`;
|
|
1007
|
-
try {
|
|
1008
|
-
const parsed = JSON.parse(data);
|
|
1009
|
-
if (parsed.error?.message) {
|
|
1010
|
-
errorMsg = parsed.error.message;
|
|
1044
|
+
else {
|
|
1045
|
+
let detail = '';
|
|
1046
|
+
try {
|
|
1047
|
+
const errParsed = JSON.parse(data);
|
|
1048
|
+
detail = errParsed.error?.message || data.substring(0, 200);
|
|
1011
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);
|
|
1012
1056
|
}
|
|
1013
|
-
|
|
1014
|
-
// ignore parse error
|
|
1015
|
-
}
|
|
1016
|
-
this.sendTo(obj.from, obj.command, { error: errorMsg }, obj.callback);
|
|
1017
|
-
}
|
|
1057
|
+
});
|
|
1018
1058
|
});
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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
|
+
}
|
|
1029
1072
|
}
|
|
1030
1073
|
break;
|
|
1031
1074
|
}
|
|
1032
1075
|
case 'testApiConnection': {
|
|
1033
|
-
// 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.
|
|
1034
1079
|
if (obj.callback) {
|
|
1035
|
-
const baseUrl = (obj.message?.baseUrl || '').trim();
|
|
1036
|
-
const apiKey = (obj.message?.apiKey || '').trim();
|
|
1037
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);
|
|
1038
1082
|
// Anthropic, Gemini, and DeepSeek always require an API key; OpenAI-compatible allows empty key with custom base URL
|
|
1039
1083
|
if (!apiKey &&
|
|
1040
1084
|
(provider === 'anthropic' || provider === 'gemini' || provider === 'deepseek' || !baseUrl)) {
|
|
@@ -1076,48 +1120,72 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1076
1120
|
}
|
|
1077
1121
|
const isHttps = urlObj.protocol === 'https:';
|
|
1078
1122
|
const requestModule = isHttps ? https : http;
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
data
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
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
|
+
}
|
|
1097
1146
|
}
|
|
1098
|
-
|
|
1099
|
-
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);
|
|
1100
1149
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1150
|
+
else if (res.statusCode === 403) {
|
|
1151
|
+
this.sendTo(obj.from, obj.command, { error: 'Access denied (403)' }, obj.callback);
|
|
1152
|
+
}
|
|
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);
|
|
1166
|
+
}
|
|
1167
|
+
});
|
|
1111
1168
|
});
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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);
|
|
1121
1189
|
}
|
|
1122
1190
|
break;
|
|
1123
1191
|
}
|
|
@@ -1281,6 +1349,10 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
1281
1349
|
this.context.allowSelfSignedCerts = this.config.allowSelfSignedCerts;
|
|
1282
1350
|
const doc = await this.getObjectViewAsync('script', 'javascript', {});
|
|
1283
1351
|
if (doc?.rows?.length) {
|
|
1352
|
+
// sort global scripts if configured
|
|
1353
|
+
if (this.config.sortGlobalScriptsAlphabetically) {
|
|
1354
|
+
doc.rows.sort((a, b) => a.value._id.localeCompare(b.value._id));
|
|
1355
|
+
}
|
|
1284
1356
|
// assemble global script
|
|
1285
1357
|
for (let g = 0; g < doc.rows.length; g++) {
|
|
1286
1358
|
const obj = doc.rows[g].value;
|
|
@@ -2145,6 +2217,9 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2145
2217
|
}
|
|
2146
2218
|
}
|
|
2147
2219
|
async stopScript(name) {
|
|
2220
|
+
if (!this.scripts[name]) {
|
|
2221
|
+
return false;
|
|
2222
|
+
}
|
|
2148
2223
|
this.log.info(`${name}: Stopping script`);
|
|
2149
2224
|
await this.setState(`scriptEnabled.${name.substring(SCRIPT_CODE_MARKER.length)}`, false, true);
|
|
2150
2225
|
if (this.messageBusHandlers[name]) {
|
|
@@ -2228,6 +2303,20 @@ class JavaScript extends adapter_core_1.Adapter {
|
|
|
2228
2303
|
for (let i = 0; i < this.scripts[name].intervals.length; i++) {
|
|
2229
2304
|
clearInterval(this.scripts[name].intervals[i]);
|
|
2230
2305
|
}
|
|
2306
|
+
// Stop all delayed states (setStateDelayed timers)
|
|
2307
|
+
for (const stateId of Object.keys(this.timers)) {
|
|
2308
|
+
if (this.timers[stateId]) {
|
|
2309
|
+
for (let i = this.timers[stateId].length - 1; i >= 0; i--) {
|
|
2310
|
+
if (this.timers[stateId][i].scriptName === name) {
|
|
2311
|
+
clearTimeout(this.timers[stateId][i].t);
|
|
2312
|
+
this.timers[stateId].splice(i, 1);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
if (!this.timers[stateId].length) {
|
|
2316
|
+
delete this.timers[stateId];
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2231
2320
|
// Stop all scheduled jobs
|
|
2232
2321
|
for (let i = 0; i < this.scripts[name].schedules.length; i++) {
|
|
2233
2322
|
if (this.scripts[name].schedules[i]) {
|