iobroker.javascript 10.1.0 → 10.1.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.
Files changed (140) hide show
  1. package/README.md +14 -9
  2. package/admin/assets/AiChatPanel-D3eFcu56.js +242 -0
  3. package/admin/assets/AiDiffView-DtbcD6Yx.js +1 -0
  4. package/admin/assets/{Debugger-DCMWjeGm.js → Debugger-DsoSdux9.js} +4 -4
  5. package/admin/assets/Error-1oeF0cix.js +1 -0
  6. package/admin/assets/Import-BH2X_ziv.js +1 -0
  7. package/admin/assets/RulesEditor-BcGsf9n4.css +1 -0
  8. package/admin/assets/RulesEditor-CJ9TvZtR.js +1 -0
  9. package/admin/assets/{ScriptEditor-CMYUt5S4.js → ScriptEditor-DNcGhbcU.js} +2 -2
  10. package/admin/assets/ScriptEditorVanillaMonaco-BBoOLFln.js +3 -0
  11. package/admin/assets/{_virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-7EPUlgCk.js → _virtual_mf-localSharedImportMap___mfe_internal__iobroker_javascript__mf_owner__1-BK6m6sZx.js} +1 -1
  12. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DezK0U6r.js +1243 -0
  13. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-_q6w9ugW.js +9 -0
  14. package/admin/assets/aiPromptBuilder-DSYziAVr.js +1 -0
  15. package/admin/assets/{blockly-C4zJerWL.js → blockly-DBw-ytY1.js} +11 -11
  16. package/admin/assets/{blocks_action-CvnOHXwt.js → blocks_action-Bpe-Zvqj.js} +1 -1
  17. package/admin/assets/{blocks_convert-BZlKcd02.js → blocks_convert-DS1wCMO_.js} +3 -3
  18. package/admin/assets/blocks_logic-DQaroQTu.js +1 -0
  19. package/admin/assets/blocks_number-C998FOz6.js +1 -0
  20. package/admin/assets/{blocks_object-B64vSfel.js → blocks_object-BGYvC_4Y.js} +1 -1
  21. package/admin/assets/blocks_procedures-BZFhrnZ3.js +3 -0
  22. package/admin/assets/blocks_sendto-BoOZkX-e.js +5 -0
  23. package/admin/assets/{blocks_switch-DViYCmQd.js → blocks_switch-CfjY75wV.js} +2 -2
  24. package/admin/assets/blocks_system-C69gZMAz.js +1 -0
  25. package/admin/assets/{blocks_text-BOvtHN2r.js → blocks_text-Dp6RaJ_p.js} +1 -1
  26. package/admin/assets/{blocks_time-BH2F-ylT.js → blocks_time-CmECitUh.js} +1 -1
  27. package/admin/assets/blocks_timeout-BI8OyiNa.js +1 -0
  28. package/admin/assets/blocks_trigger-X7O3INGz.js +2 -0
  29. package/admin/assets/blocks_words-BhHIf_1o.js +1 -0
  30. package/admin/assets/{cronHoverProvider-DjY3jLvQ.js → cronHoverProvider-BhX-SvkB.js} +1 -1
  31. package/admin/assets/{dist-D6o4h8Pd.js → dist-CaOsN0XW.js} +1 -1
  32. package/admin/assets/{field_cron-BMQWPb61.js → field_cron-BJLkgNzf.js} +1 -1
  33. package/admin/assets/{field_oid-BGIMv9gw.js → field_oid-CJZIeruf.js} +1 -1
  34. package/admin/assets/{field_script-CdzYBCe6.js → field_script-4IGidWJ7.js} +1 -1
  35. package/admin/assets/helpers-DaSC8t53.js +1 -0
  36. package/admin/assets/{hostInit-DBlZi_Kt.js → hostInit-B3IjMQKW.js} +1 -1
  37. package/admin/assets/index-DKhQ45Yn.js +871 -0
  38. package/admin/assets/index-Z8Hkv58g.css +1 -0
  39. package/admin/assets/{inlineChatWidget-SDoyVtOR.js → inlineChatWidget-CBVgT6FZ.js} +1 -1
  40. package/admin/assets/{inlineDiffController-CnHE3Rwd.js → inlineDiffController-DianrKl1.js} +1 -1
  41. package/admin/assets/{mf-entry-bootstrap-0-a03ecebd.js → mf-entry-bootstrap-0-a65094d3.js} +3 -2
  42. package/admin/assets/stateHoverProvider-CIFNrLee.js +9 -0
  43. package/admin/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__iobroker_javascript__remoteEntry_js-BO-WKj5V.js +1 -0
  44. package/admin/custom/assets/Components-BCQTjBgm.js +1 -0
  45. package/admin/custom/assets/{_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-DljJyQQg.js → _virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1-BuAc8OyX.js} +1 -1
  46. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-BGJv4zZP.js +1 -0
  47. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-BD2OkSge.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-B4E6MFJ4.js} +1 -1
  48. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-pM642JFd.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-aRlxyEhq.js} +86 -86
  49. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-DQwzYx75.js +1494 -0
  50. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__leaflet__loadShare__.js-h-rCw3iz.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__leaflet__loadShare__.js-n2zIPi0L.js} +1 -1
  51. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-xxszBNje.js +9 -0
  52. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__date_mf_2_fns__prebuild__-D5hqXJXI.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__date_mf_2_fns__prebuild__-Tq7tesSS.js} +1 -1
  53. package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__leaflet__prebuild__-DARUoA03.js → _virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__prebuild__leaflet__prebuild__-BeF0jEWM.js} +1 -1
  54. package/admin/custom/assets/{hostInit-0DGPuZTB.js → hostInit-CHwrowP4.js} +1 -1
  55. package/admin/custom/assets/{index-DcFI5r-g.js → index-Cm6xl9K4.js} +1 -1
  56. package/admin/custom/assets/{lib-BlUjuGH7.js → lib-bhrFL6jR.js} +1 -1
  57. package/admin/custom/assets/{mf-entry-bootstrap-0-efd847a6.js → mf-entry-bootstrap-0-f08941e2.js} +3 -2
  58. package/admin/custom/assets/{setYear-BaCcPbbI.js → setYear-DzTZ2h9A.js} +1 -1
  59. package/admin/custom/assets/virtualExposes-B5LJmnfO.js +1 -0
  60. package/admin/custom/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-ML-chjs6.js +1 -0
  61. package/admin/custom/assets/{virtual_mf-exposes___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-DPck7zEs.js → virtual_mf-exposes___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-C3ImybiM.js} +2 -2
  62. package/admin/custom/customComponents.js +12 -1
  63. package/admin/custom/i18n/de.json +11 -1
  64. package/admin/custom/i18n/en.json +11 -1
  65. package/admin/custom/i18n/es.json +11 -1
  66. package/admin/custom/i18n/fr.json +11 -1
  67. package/admin/custom/i18n/it.json +11 -1
  68. package/admin/custom/i18n/nl.json +11 -1
  69. package/admin/custom/i18n/pl.json +11 -1
  70. package/admin/custom/i18n/pt.json +11 -1
  71. package/admin/custom/i18n/ru.json +11 -1
  72. package/admin/custom/i18n/uk.json +11 -1
  73. package/admin/custom/i18n/zh-cn.json +11 -1
  74. package/admin/i18n/de.json +3 -0
  75. package/admin/i18n/en.json +3 -0
  76. package/admin/i18n/es.json +3 -0
  77. package/admin/i18n/fr.json +3 -0
  78. package/admin/i18n/it.json +3 -0
  79. package/admin/i18n/nl.json +3 -0
  80. package/admin/i18n/pl.json +3 -0
  81. package/admin/i18n/pt.json +3 -0
  82. package/admin/i18n/ru.json +3 -0
  83. package/admin/i18n/uk.json +3 -0
  84. package/admin/i18n/zh-cn.json +3 -0
  85. package/admin/jsonConfig.json +31 -0
  86. package/admin/mf-manifest.json +1 -1
  87. package/admin/mf-stats.json +1 -1
  88. package/admin/remoteEntry.js +12 -1
  89. package/admin/tab.html +6 -15
  90. package/build/lib/javascript.d.ts +48 -0
  91. package/build/lib/sandbox.js +2 -0
  92. package/build/lib/sandbox.js.map +1 -1
  93. package/build/lib/secrets.js +220 -0
  94. package/build/lib/secrets.js.map +1 -0
  95. package/build/main.js +51 -0
  96. package/build/main.js.map +1 -1
  97. package/build/types.d.ts +7 -0
  98. package/docs/en/blockly.md +29 -0
  99. package/docs/en/javascript.md +56 -0
  100. package/io-package.json +28 -27
  101. package/package.json +2 -2
  102. package/admin/assets/AiChatPanel-C4kuqP2U.js +0 -237
  103. package/admin/assets/AiDiffView-zJTEnv05.js +0 -1
  104. package/admin/assets/AiScriptAnalyzer--BHML2rN.js +0 -9
  105. package/admin/assets/BlocklyEditor-BB77aFJI.js +0 -84
  106. package/admin/assets/ContextWrapper-C2yvd2Tn.css +0 -1
  107. package/admin/assets/ContextWrapper-W2H0z6zD.js +0 -136
  108. package/admin/assets/Error-CBC2oZzb.js +0 -1
  109. package/admin/assets/Import-B-F9IhZ-.js +0 -1
  110. package/admin/assets/RulesEditor-ByjgEaVo.js +0 -1
  111. package/admin/assets/RulesEditor-k-DMHotH.css +0 -1
  112. package/admin/assets/ScriptEditorVanillaMonaco-v12-gORS.js +0 -3
  113. package/admin/assets/Send-CChqR5Oy.js +0 -1
  114. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-DuplUOZN.js +0 -1243
  115. package/admin/assets/_virtual_mf___mfe_internal__iobroker_javascript__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-JkwfVf8d.js +0 -9
  116. package/admin/assets/aiCodeLensProvider-BGvIu6hH.js +0 -4
  117. package/admin/assets/aiPromptBuilder-Bs27qOHT.js +0 -7
  118. package/admin/assets/back-CXiqI7Po.jpg +0 -0
  119. package/admin/assets/blocklyDarkTheme-DEiAX5DT.js +0 -1
  120. package/admin/assets/blocks_logic-B33Pypxr.js +0 -1
  121. package/admin/assets/blocks_number-k2IjE7OH.js +0 -1
  122. package/admin/assets/blocks_procedures-BpvNI5dO.js +0 -3
  123. package/admin/assets/blocks_sendto-pwZchfe0.js +0 -5
  124. package/admin/assets/blocks_system-BZZcGqTJ.js +0 -1
  125. package/admin/assets/blocks_timeout-C3BoW4XJ.js +0 -1
  126. package/admin/assets/blocks_trigger-gwXvuAHV.js +0 -2
  127. package/admin/assets/blocks_words-D8PL_nSE.js +0 -1
  128. package/admin/assets/helpers-Crmcgphg.js +0 -1
  129. package/admin/assets/index-BHhhebRG.css +0 -1
  130. package/admin/assets/index-BvPVHv3P.js +0 -192
  131. package/admin/assets/javascript-DVIhlTDr.js +0 -218
  132. package/admin/assets/mjs-Bh7xFIpd.js +0 -90
  133. package/admin/assets/stateHoverProvider-D0iXo6YF.js +0 -9
  134. package/admin/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__iobroker_javascript__remoteEntry_js-DYEYnOkt.js +0 -1
  135. package/admin/custom/assets/Components-CuFt9lAY.js +0 -1
  136. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-D0M1SjIE.js +0 -1
  137. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare___mf_0_iobroker_mf_1_json_mf_2_config__loadShare__.js-BLelmJ7Y.js +0 -1494
  138. package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomJavascriptSet__mf_owner__1__loadShare__react_mf_2_dom_mf_1_client__loadShare__.js-BGiB1l5e.js +0 -9
  139. package/admin/custom/assets/virtualExposes-DGxNcyb6.js +0 -1
  140. package/admin/custom/assets/virtual_mf-REMOTE_ENTRY_ID___mfe_internal__ConfigCustomJavascriptSet__customComponents_js-DVNwFcUr.js +0 -1
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SecretsManager = exports.SECRETS_PREFIX = void 0;
37
+ exports.isSecretId = isSecretId;
38
+ exports.getSecretName = getSecretName;
39
+ /**
40
+ * Access to the central ioBroker credential store (`system.credentials.*`) from scripts.
41
+ *
42
+ * The admin UI manages the credentials under "Basic settings" → "Credentials". Every credential
43
+ * has an ID like `system.credentials.CameraPassword` and holds either a single `key` (API keys)
44
+ * or a `login`/`password` pair. The secret fields are stored encrypted with the system secret.
45
+ *
46
+ * This manager keeps all credentials decrypted in memory and exposes them to the scripts as the
47
+ * global `SECRETS` object, so a script can simply write:
48
+ *
49
+ * ```js
50
+ * const password = SECRETS.CameraPassword.key;
51
+ * ```
52
+ *
53
+ * The cache is kept up to date while the adapter is running, so editing a credential in the admin
54
+ * UI takes effect immediately - without restarting the adapter or the scripts.
55
+ */
56
+ // Imported from its own entry point on purpose: the package index of `@iobroker/adapter-core`
57
+ // looks for js-controller while it is being loaded and terminates the process with exit code 10
58
+ // when it is missing. This module is required by the unit tests, which run without a controller,
59
+ // so it must stay clear of that side effect. `@iobroker/adapter-core/credentials` is free of it -
60
+ // it is written to be usable in browser builds as well.
61
+ const Credentials = __importStar(require("@iobroker/adapter-core/credentials"));
62
+ /** Prefix of all credential object IDs */
63
+ exports.SECRETS_PREFIX = 'system.credentials.';
64
+ /**
65
+ * Checks if the given object ID belongs to the central credential store.
66
+ *
67
+ * @param id Object ID, e.g. `system.credentials.CameraPassword`
68
+ */
69
+ function isSecretId(id) {
70
+ return id.startsWith(exports.SECRETS_PREFIX) && id.length > exports.SECRETS_PREFIX.length;
71
+ }
72
+ /**
73
+ * Extracts the name a credential is exposed under in `SECRETS`.
74
+ *
75
+ * @param id Object ID, e.g. `system.credentials.CameraPassword`
76
+ */
77
+ function getSecretName(id) {
78
+ return id.substring(exports.SECRETS_PREFIX.length);
79
+ }
80
+ class SecretsManager {
81
+ adapter;
82
+ /** Decrypted credentials, keyed by name (the credential ID without the `system.credentials.` prefix) */
83
+ cache = new Map();
84
+ /** If the scripts may read the credentials at all (adapter option `enableSecrets`) */
85
+ enabled = false;
86
+ /** Warn only once per script run that the feature is disabled */
87
+ disabledWarningShown = false;
88
+ /**
89
+ * The object handed to the scripts as the global `SECRETS`.
90
+ *
91
+ * It is a read-only view on the cache, so the values stay up to date without the scripts
92
+ * having to re-read anything, and a script cannot modify the credentials of another script.
93
+ */
94
+ secrets;
95
+ constructor(adapter) {
96
+ this.adapter = adapter;
97
+ this.secrets = new Proxy(Object.create(null), {
98
+ get: (_target, prop) => typeof prop === 'string' ? this.get(prop) : undefined,
99
+ has: (_target, prop) => typeof prop === 'string' && this.cache.has(prop),
100
+ ownKeys: () => (this.enabled ? Array.from(this.cache.keys()) : []),
101
+ getOwnPropertyDescriptor: (_target, prop) => {
102
+ const value = typeof prop === 'string' ? this.get(prop) : undefined;
103
+ return value === undefined
104
+ ? undefined
105
+ : { value, enumerable: true, configurable: true, writable: false };
106
+ },
107
+ // The credentials belong to the system - scripts may read them, but never change them
108
+ set: () => false,
109
+ defineProperty: () => false,
110
+ deleteProperty: () => false,
111
+ });
112
+ }
113
+ /** Reads one credential from the cache and warns if the whole feature is switched off */
114
+ get(name) {
115
+ if (!this.enabled) {
116
+ if (!this.disabledWarningShown) {
117
+ this.disabledWarningShown = true;
118
+ this.adapter.log.warn(`A script tried to read the secret "${name}", but the access to the credentials is disabled. Enable "Allow scripts to read the credentials" in the instance settings.`);
119
+ }
120
+ return undefined;
121
+ }
122
+ return this.cache.get(name);
123
+ }
124
+ /**
125
+ * Reads all credentials of the central store and decrypts them.
126
+ *
127
+ * @param enabled Value of the adapter option `enableSecrets`
128
+ */
129
+ async load(enabled) {
130
+ this.cache.clear();
131
+ this.disabledWarningShown = false;
132
+ this.enabled = enabled;
133
+ if (!enabled) {
134
+ return;
135
+ }
136
+ if (!Credentials?.listCredentials) {
137
+ this.adapter.log.warn('Cannot read the credentials for "SECRETS": the credentials API is only available with js-controller 7.2 or newer');
138
+ this.enabled = false;
139
+ return;
140
+ }
141
+ try {
142
+ const list = await Credentials.listCredentials(this.adapter);
143
+ for (const entry of list) {
144
+ await this.update(entry.id);
145
+ }
146
+ this.adapter.log.debug(`${this.cache.size} secret(s) available in the scripts as "SECRETS"`);
147
+ }
148
+ catch (e) {
149
+ this.adapter.log.warn(`Cannot read the credentials: ${e instanceof Error ? e.message : String(e)}`);
150
+ }
151
+ }
152
+ /**
153
+ * Reads one credential and decrypts it. Called for every change of a `system.credentials.*`
154
+ * object, so edits in the admin UI are visible in the scripts immediately.
155
+ *
156
+ * @param id Object ID, e.g. `system.credentials.CameraPassword`
157
+ */
158
+ async update(id) {
159
+ if (!this.enabled || !isSecretId(id)) {
160
+ return;
161
+ }
162
+ const name = getSecretName(id);
163
+ try {
164
+ const credentials = await Credentials.getCredentials(this.adapter, id);
165
+ this.cache.set(name, Object.freeze({ ...credentials.values }));
166
+ }
167
+ catch (e) {
168
+ this.cache.delete(name);
169
+ this.adapter.log.warn(`Cannot read the secret "${name}": ${e instanceof Error ? e.message : String(e)}`);
170
+ }
171
+ }
172
+ /**
173
+ * Forgets one credential because its object was deleted.
174
+ *
175
+ * @param id Object ID, e.g. `system.credentials.CameraPassword`
176
+ */
177
+ remove(id) {
178
+ if (isSecretId(id) && this.cache.delete(getSecretName(id))) {
179
+ this.adapter.log.debug(`Secret "${getSecretName(id)}" was deleted`);
180
+ }
181
+ }
182
+ /**
183
+ * The structure of the credential store without any secret value: which credentials exist and
184
+ * which fields each of them has. Used by the instance settings and the Blockly editor to show
185
+ * the user what `SECRETS.<name>.<field>` expressions are available.
186
+ */
187
+ getStructure() {
188
+ return Array.from(this.cache.entries())
189
+ .map(([name, values]) => ({ name, fields: Object.keys(values).sort() }))
190
+ .sort((a, b) => a.name.localeCompare(b.name));
191
+ }
192
+ /**
193
+ * Builds the ambient TypeScript declarations for the credentials that currently exist, so the
194
+ * editor can suggest their names after typing `SECRETS.` and their fields after the next dot.
195
+ * The names are merged into the `iobJS.Secrets` interface, which has an index signature for
196
+ * all other names.
197
+ */
198
+ getDeclarations() {
199
+ const lines = [
200
+ '// Generated by ioBroker.javascript from the central credential store - do not edit',
201
+ 'declare namespace iobJS {',
202
+ ' interface Secrets {',
203
+ ];
204
+ for (const { name, fields } of this.getStructure()) {
205
+ const values = this.cache.get(name);
206
+ // Declare the fields this credential really has, so the editor suggests exactly those
207
+ const members = fields.map(field => `${JSON.stringify(field)}: ${typeof values[field]}`).join('; ');
208
+ lines.push(` ${JSON.stringify(name)}: { ${members} };`);
209
+ }
210
+ lines.push(' }', '}', '');
211
+ return lines.join('\n');
212
+ }
213
+ /** Forgets all decrypted credentials */
214
+ destroy() {
215
+ this.cache.clear();
216
+ this.enabled = false;
217
+ }
218
+ }
219
+ exports.SecretsManager = SecretsManager;
220
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../src/lib/secrets.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access to the central ioBroker credential store (`system.credentials.*`) from scripts.\n *\n * The admin UI manages the credentials under \"Basic settings\" → \"Credentials\". Every credential\n * has an ID like `system.credentials.CameraPassword` and holds either a single `key` (API keys)\n * or a `login`/`password` pair. The secret fields are stored encrypted with the system secret.\n *\n * This manager keeps all credentials decrypted in memory and exposes them to the scripts as the\n * global `SECRETS` object, so a script can simply write:\n *\n * ```js\n * const password = SECRETS.CameraPassword.key;\n * ```\n *\n * The cache is kept up to date while the adapter is running, so editing a credential in the admin\n * UI takes effect immediately - without restarting the adapter or the scripts.\n */\n// Imported from its own entry point on purpose: the package index of `@iobroker/adapter-core`\n// looks for js-controller while it is being loaded and terminates the process with exit code 10\n// when it is missing. This module is required by the unit tests, which run without a controller,\n// so it must stay clear of that side effect. `@iobroker/adapter-core/credentials` is free of it -\n// it is written to be usable in browser builds as well.\nimport * as Credentials from '@iobroker/adapter-core/credentials';\n\n/** Prefix of all credential object IDs */\nexport const SECRETS_PREFIX = 'system.credentials.';\n\n/** Decrypted fields of one credential, e.g. `{ key: 'abc' }` or `{ login: 'user', password: 'pass' }` */\nexport type SecretValues = Record<string, string | number | boolean>;\n\n/** The global `SECRETS` object: credential name (ID without the prefix) => decrypted fields */\nexport type Secrets = Record<string, SecretValues>;\n\n/** One credential without its values: only the name and the names of its fields */\nexport interface SecretStructure {\n /** Credential name, i.e. the object ID without the `system.credentials.` prefix */\n name: string;\n /** Names of the fields this credential has, e.g. `['key']` or `['login', 'password']` */\n fields: string[];\n}\n\n/**\n * Checks if the given object ID belongs to the central credential store.\n *\n * @param id Object ID, e.g. `system.credentials.CameraPassword`\n */\nexport function isSecretId(id: string): boolean {\n return id.startsWith(SECRETS_PREFIX) && id.length > SECRETS_PREFIX.length;\n}\n\n/**\n * Extracts the name a credential is exposed under in `SECRETS`.\n *\n * @param id Object ID, e.g. `system.credentials.CameraPassword`\n */\nexport function getSecretName(id: string): string {\n return id.substring(SECRETS_PREFIX.length);\n}\n\nexport class SecretsManager {\n private readonly adapter: ioBroker.Adapter;\n\n /** Decrypted credentials, keyed by name (the credential ID without the `system.credentials.` prefix) */\n private readonly cache: Map<string, Readonly<SecretValues>> = new Map();\n\n /** If the scripts may read the credentials at all (adapter option `enableSecrets`) */\n private enabled = false;\n\n /** Warn only once per script run that the feature is disabled */\n private disabledWarningShown = false;\n\n /**\n * The object handed to the scripts as the global `SECRETS`.\n *\n * It is a read-only view on the cache, so the values stay up to date without the scripts\n * having to re-read anything, and a script cannot modify the credentials of another script.\n */\n public readonly secrets: Secrets;\n\n constructor(adapter: ioBroker.Adapter) {\n this.adapter = adapter;\n\n this.secrets = new Proxy(Object.create(null) as Secrets, {\n get: (_target: Secrets, prop: string | symbol): SecretValues | undefined =>\n typeof prop === 'string' ? this.get(prop) : undefined,\n has: (_target: Secrets, prop: string | symbol): boolean => typeof prop === 'string' && this.cache.has(prop),\n ownKeys: (): string[] => (this.enabled ? Array.from(this.cache.keys()) : []),\n getOwnPropertyDescriptor: (_target: Secrets, prop: string | symbol): PropertyDescriptor | undefined => {\n const value = typeof prop === 'string' ? this.get(prop) : undefined;\n return value === undefined\n ? undefined\n : { value, enumerable: true, configurable: true, writable: false };\n },\n // The credentials belong to the system - scripts may read them, but never change them\n set: (): boolean => false,\n defineProperty: (): boolean => false,\n deleteProperty: (): boolean => false,\n });\n }\n\n /** Reads one credential from the cache and warns if the whole feature is switched off */\n private get(name: string): SecretValues | undefined {\n if (!this.enabled) {\n if (!this.disabledWarningShown) {\n this.disabledWarningShown = true;\n this.adapter.log.warn(\n `A script tried to read the secret \"${name}\", but the access to the credentials is disabled. Enable \"Allow scripts to read the credentials\" in the instance settings.`,\n );\n }\n return undefined;\n }\n return this.cache.get(name);\n }\n\n /**\n * Reads all credentials of the central store and decrypts them.\n *\n * @param enabled Value of the adapter option `enableSecrets`\n */\n async load(enabled: boolean): Promise<void> {\n this.cache.clear();\n this.disabledWarningShown = false;\n this.enabled = enabled;\n\n if (!enabled) {\n return;\n }\n\n if (!Credentials?.listCredentials) {\n this.adapter.log.warn(\n 'Cannot read the credentials for \"SECRETS\": the credentials API is only available with js-controller 7.2 or newer',\n );\n this.enabled = false;\n return;\n }\n\n try {\n const list = await Credentials.listCredentials(this.adapter);\n for (const entry of list) {\n await this.update(entry.id);\n }\n this.adapter.log.debug(`${this.cache.size} secret(s) available in the scripts as \"SECRETS\"`);\n } catch (e: unknown) {\n this.adapter.log.warn(`Cannot read the credentials: ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Reads one credential and decrypts it. Called for every change of a `system.credentials.*`\n * object, so edits in the admin UI are visible in the scripts immediately.\n *\n * @param id Object ID, e.g. `system.credentials.CameraPassword`\n */\n async update(id: string): Promise<void> {\n if (!this.enabled || !isSecretId(id)) {\n return;\n }\n const name = getSecretName(id);\n\n try {\n const credentials = await Credentials.getCredentials(this.adapter, id);\n this.cache.set(name, Object.freeze({ ...credentials.values }));\n } catch (e: unknown) {\n this.cache.delete(name);\n this.adapter.log.warn(`Cannot read the secret \"${name}\": ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n /**\n * Forgets one credential because its object was deleted.\n *\n * @param id Object ID, e.g. `system.credentials.CameraPassword`\n */\n remove(id: string): void {\n if (isSecretId(id) && this.cache.delete(getSecretName(id))) {\n this.adapter.log.debug(`Secret \"${getSecretName(id)}\" was deleted`);\n }\n }\n\n /**\n * The structure of the credential store without any secret value: which credentials exist and\n * which fields each of them has. Used by the instance settings and the Blockly editor to show\n * the user what `SECRETS.<name>.<field>` expressions are available.\n */\n getStructure(): SecretStructure[] {\n return Array.from(this.cache.entries())\n .map(([name, values]) => ({ name, fields: Object.keys(values).sort() }))\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /**\n * Builds the ambient TypeScript declarations for the credentials that currently exist, so the\n * editor can suggest their names after typing `SECRETS.` and their fields after the next dot.\n * The names are merged into the `iobJS.Secrets` interface, which has an index signature for\n * all other names.\n */\n getDeclarations(): string {\n const lines = [\n '// Generated by ioBroker.javascript from the central credential store - do not edit',\n 'declare namespace iobJS {',\n ' interface Secrets {',\n ];\n for (const { name, fields } of this.getStructure()) {\n const values = this.cache.get(name)!;\n // Declare the fields this credential really has, so the editor suggests exactly those\n const members = fields.map(field => `${JSON.stringify(field)}: ${typeof values[field]}`).join('; ');\n lines.push(` ${JSON.stringify(name)}: { ${members} };`);\n }\n lines.push(' }', '}', '');\n return lines.join('\\n');\n }\n\n /** Forgets all decrypted credentials */\n destroy(): void {\n this.cache.clear();\n this.enabled = false;\n }\n}\n"]}
package/build/main.js CHANGED
@@ -78,6 +78,7 @@ 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 secrets_1 = require("./lib/secrets");
81
82
  const aiProviderResolver_1 = require("./lib/aiProviderResolver");
82
83
  const anthropicAdapter_1 = require("./lib/anthropicAdapter");
83
84
  const eventObj_1 = require("./lib/eventObj");
@@ -328,6 +329,11 @@ class JavaScript extends adapter_core_1.Adapter {
328
329
  aiCredentialCache = new Map();
329
330
  /** Unsubscribe callbacks for the AI credential subscriptions (manager mode). */
330
331
  aiCredentialUnsubscribers = [];
332
+ /**
333
+ * Decrypted credentials of the central ioBroker credential store (`system.credentials.*`),
334
+ * exposed to the scripts as the global `SECRETS` object. Kept up to date by `onObjectChange`.
335
+ */
336
+ secretsManager = new secrets_1.SecretsManager(this);
331
337
  globalScript = '';
332
338
  /** Generated declarations for global TypeScripts */
333
339
  globalDeclarations = '';
@@ -479,6 +485,7 @@ class JavaScript extends adapter_core_1.Adapter {
479
485
  adapter: this,
480
486
  logError: this.logError.bind(this),
481
487
  allowSelfSignedCerts: false,
488
+ secrets: this.secretsManager.secrets,
482
489
  };
483
490
  this.tsServer = new virtual_tsc_1.Server(typescriptSettings_1.tsCompilerOptions, this.tsLog);
484
491
  }
@@ -509,6 +516,16 @@ class JavaScript extends adapter_core_1.Adapter {
509
516
  this._enums.delete(id);
510
517
  }
511
518
  }
519
+ // Keep the decrypted credentials (global `SECRETS`) in sync with the central credential store
520
+ if ((0, secrets_1.isSecretId)(id)) {
521
+ if (obj) {
522
+ await this.secretsManager.update(id);
523
+ }
524
+ else {
525
+ this.secretsManager.remove(id);
526
+ }
527
+ this.updateSecretsDeclarations();
528
+ }
512
529
  // IO-9: Invalidate sendTo instance-cache when adapter instances change
513
530
  if (id.startsWith('system.adapter.')) {
514
531
  const parts = id.split('.');
@@ -777,6 +794,7 @@ class JavaScript extends adapter_core_1.Adapter {
777
794
  this.setStateCountCheckInterval = null;
778
795
  }
779
796
  await this.unsubscribeAiCredentials();
797
+ this.secretsManager.destroy();
780
798
  await this.stopAllScripts();
781
799
  }
782
800
  catch (err) {
@@ -1401,6 +1419,18 @@ class JavaScript extends adapter_core_1.Adapter {
1401
1419
  }
1402
1420
  break;
1403
1421
  }
1422
+ case 'getSecrets': {
1423
+ // Reports which credentials exist and which fields they have, so the instance
1424
+ // settings and the Blockly editor can show the available `SECRETS.<name>.<field>`
1425
+ // expressions. The decrypted values never leave the backend.
1426
+ if (obj.callback) {
1427
+ this.sendTo(obj.from, obj.command, {
1428
+ enabled: this.config.enableSecrets !== false,
1429
+ secrets: this.secretsManager.getStructure(),
1430
+ }, obj.callback);
1431
+ }
1432
+ break;
1433
+ }
1404
1434
  case 'prettier': {
1405
1435
  // Format the code with Prettier
1406
1436
  if (obj.message && typeof obj.message.code === 'string') {
@@ -1588,6 +1618,10 @@ class JavaScript extends adapter_core_1.Adapter {
1588
1618
  // In `manager` credential mode, subscribe to the configured AI credentials so changes in the
1589
1619
  // central credential store are picked up live (the keys are cached for the AI sendTo handlers).
1590
1620
  await this.subscribeAiCredentials();
1621
+ // Read the central credential store, so the scripts can use `SECRETS.<name>.<field>`.
1622
+ // Later changes are picked up in `onObjectChange`.
1623
+ await this.secretsManager.load(this.config.enableSecrets !== false);
1624
+ this.updateSecretsDeclarations();
1591
1625
  const doc = await this.getObjectViewAsync('script', 'javascript', {});
1592
1626
  if (doc?.rows?.length) {
1593
1627
  // sort global scripts if configured
@@ -1759,6 +1793,23 @@ class JavaScript extends adapter_core_1.Adapter {
1759
1793
  }
1760
1794
  }, 60_000).unref();
1761
1795
  }
1796
+ /**
1797
+ * Publishes the names of the existing credentials as ambient declarations, so the editor can
1798
+ * suggest them after typing `SECRETS.` and the TypeScript compiler knows them.
1799
+ */
1800
+ updateSecretsDeclarations() {
1801
+ if (!tsAmbient) {
1802
+ return;
1803
+ }
1804
+ const declarationPath = 'secrets.d.ts';
1805
+ const declarations = this.secretsManager.getDeclarations();
1806
+ if (tsAmbient[declarationPath] === declarations) {
1807
+ return;
1808
+ }
1809
+ tsAmbient[declarationPath] = declarations;
1810
+ this.tsServer.provideAmbientDeclarations({ [declarationPath]: declarations });
1811
+ jsDeclarationServer.provideAmbientDeclarations({ [declarationPath]: declarations });
1812
+ }
1762
1813
  loadTypeScriptDeclarations() {
1763
1814
  // try to load the typings on disk for all 3rd party modules
1764
1815
  const packages = [