smart-home-engine 1.1.5 → 1.1.6

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.
@@ -1,4 +1,4 @@
1
- import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-BcELVSd4.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-DRKYxwDj.js";/*!-----------------------------------------------------------------------------
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
4
4
  * Released under the MIT license
@@ -172,7 +172,7 @@
172
172
  }
173
173
  })();
174
174
  </script>
175
- <script type="module" crossorigin src="/assets/index-BcELVSd4.js"></script>
175
+ <script type="module" crossorigin src="/assets/index-DRKYxwDj.js"></script>
176
176
  <link rel="modulepreload" crossorigin href="/assets/monaco-langs-BW2J83t5.js">
177
177
  <link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
178
178
  <link rel="stylesheet" crossorigin href="/assets/index-iTrR9H4f.css">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-home-engine",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "Node.js based script runner for use in MQTT based Smart Home environments",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/index.js CHANGED
@@ -119,6 +119,7 @@ if (typeof config.port !== 'undefined') {
119
119
  log.error('http server start failed:', err.message);
120
120
  process.exit(1);
121
121
  });
122
+ require('./web/broker-api').setLogger(log);
122
123
  }
123
124
 
124
125
  const chokidar = require('chokidar');
package/src/lib/dynsec.js CHANGED
@@ -39,9 +39,14 @@ function _drain() {
39
39
  const { command, payload, resolve, reject } = _queue.shift();
40
40
  _inflight = true;
41
41
 
42
+ const safePayload = { ...payload };
43
+ if ('password' in safePayload) safePayload.password = '***';
44
+ if (_log) _log.debug(`dynsec: → sending "${command}"`, JSON.stringify(safePayload));
45
+
42
46
  const timer = setTimeout(() => {
43
47
  _inflight = false;
44
48
  _inflightResolve = null;
49
+ if (_log) _log.debug(`dynsec: timeout waiting for response to "${command}" (${_timeout}ms)`);
45
50
  reject(new Error(`dynsec timeout waiting for response to "${command}"`));
46
51
  _drain();
47
52
  }, _timeout);
@@ -52,8 +57,10 @@ function _drain() {
52
57
  _inflightResolve = null;
53
58
  const r = responses.find((resp) => resp.command === command);
54
59
  if (r && r.error) {
60
+ if (_log) _log.debug(`dynsec: ✕ "${command}" error: ${r.error}`);
55
61
  reject(new Error(r.error));
56
62
  } else {
63
+ if (_log) _log.debug(`dynsec: ✓ "${command}" ok`);
57
64
  resolve(r || {});
58
65
  }
59
66
  _drain();
@@ -67,8 +74,12 @@ function _request(command, payload = {}) {
67
74
  return Promise.reject(new Error('she.broker: dynsec not configured — set broker.dynsec in config.json'));
68
75
  }
69
76
  if (!_connected) {
77
+ if (_log) _log.debug(`dynsec: request "${command}" rejected — not connected (queue length: ${_queue.length})`);
70
78
  return Promise.reject(new Error('she.broker: dynsec not connected'));
71
79
  }
80
+ const safePayload = { ...payload };
81
+ if ('password' in safePayload) safePayload.password = '***';
82
+ if (_log) _log.debug(`dynsec: queuing "${command}" (queue length: ${_queue.length}, inflight: ${_inflight})`, JSON.stringify(safePayload));
72
83
  return new Promise((resolve, reject) => {
73
84
  _queue.push({ command, payload, resolve, reject });
74
85
  _drain();
@@ -139,8 +150,12 @@ function init(config, log) {
139
150
  _log.error('dynsec: invalid JSON on response topic');
140
151
  return;
141
152
  }
153
+ const cmds = Array.isArray(msg.responses) ? msg.responses.map((r) => r.command).join(', ') : 'none';
154
+ if (_log) _log.debug(`dynsec: ← response received, commands: [${cmds}]`);
142
155
  if (_inflightResolve && Array.isArray(msg.responses)) {
143
156
  _inflightResolve(msg.responses);
157
+ } else if (!_inflightResolve) {
158
+ if (_log) _log.debug(`dynsec: unexpected response (no inflight request), commands: [${cmds}]`);
144
159
  }
145
160
  });
146
161
  }
@@ -27,6 +27,13 @@ const DEFAULT_SSH_KEY = path.join(sheConfig['data-dir'], 'ssh', 'broker_id_ed255
27
27
 
28
28
  const router = express.Router();
29
29
 
30
+ let _log = null;
31
+
32
+ /** Must be called once from index.js so broker-api can emit debug-level log lines. */
33
+ function setLogger(log) {
34
+ _log = log;
35
+ }
36
+
30
37
  // ── Helpers ────────────────────────────────────────────────────────────────────
31
38
 
32
39
  /** Get broker config from live config.json */
@@ -191,12 +198,18 @@ router.post('/reload', async (req, res) => {
191
198
  try {
192
199
  const bc = getBrokerConfig(req);
193
200
  if (bc.ssh && bc.ssh.host) {
201
+ const cmd = bc.reloadCmd || 'sudo systemctl reload mosquitto';
202
+ _log?.debug(`broker: remote reload on ${bc.ssh.host}: ${cmd}`);
194
203
  const result = await sshDeploy.runCommand(bc.ssh, cmd);
204
+ _log?.debug(`broker: remote reload stdout=${result.stdout} stderr=${result.stderr}`);
195
205
  return res.json({ ok: true, ...result });
196
206
  }
207
+ _log?.debug('broker: local reload mosquitto');
197
208
  const result = await mosquittoConf.reload(bc);
209
+ _log?.debug(`broker: local reload stdout=${result.stdout} stderr=${result.stderr}`);
198
210
  res.json({ ok: true, stdout: result.stdout, stderr: result.stderr });
199
211
  } catch (err) {
212
+ _log?.debug(`broker: reload error: ${err.message}`);
200
213
  handleError(res, err);
201
214
  }
202
215
  });
@@ -211,12 +224,17 @@ router.post('/restart', async (req, res) => {
211
224
  const bc = getBrokerConfig(req);
212
225
  if (bc.ssh && bc.ssh.host) {
213
226
  const cmd = bc.restartCmd || 'sudo systemctl restart mosquitto';
227
+ _log?.debug(`broker: remote restart on ${bc.ssh.host}: ${cmd}`);
214
228
  const result = await sshDeploy.runCommand(bc.ssh, cmd);
229
+ _log?.debug(`broker: remote restart stdout=${result.stdout} stderr=${result.stderr}`);
215
230
  return res.json({ ok: true, ...result });
216
231
  }
232
+ _log?.debug('broker: local restart mosquitto');
217
233
  const result = await mosquittoConf.restart(bc);
234
+ _log?.debug(`broker: local restart stdout=${result.stdout} stderr=${result.stderr}`);
218
235
  res.json({ ok: true, stdout: result.stdout, stderr: result.stderr });
219
236
  } catch (err) {
237
+ _log?.debug(`broker: restart error: ${err.message}`);
220
238
  handleError(res, err);
221
239
  }
222
240
  });
@@ -655,7 +673,7 @@ router.delete('/ca/trusted/:fingerprint', async (req, res) => {
655
673
  }
656
674
  });
657
675
 
658
- module.exports = { router };
676
+ module.exports = { router, setLogger };
659
677
 
660
678
  // ── SSH routes ─────────────────────────────────────────────────────────────────
661
679
  // Note: these routes are mounted on the same router but defined after module.exports
@@ -680,9 +698,12 @@ router.post('/ssh/keygen', async (req, res) => {
680
698
  try {
681
699
  const bc = getBrokerConfig(req);
682
700
  const identityFile = (bc.ssh && bc.ssh.identityFile) || DEFAULT_SSH_KEY;
701
+ _log?.debug(`broker: generating SSH keypair at ${identityFile}`);
683
702
  const publicKey = await sshDeploy.generateKeypair(identityFile);
703
+ _log?.debug('broker: SSH keypair generated ok');
684
704
  res.json({ ok: true, publicKey });
685
705
  } catch (err) {
706
+ _log?.debug(`broker: SSH keygen error: ${err.message}`);
686
707
  handleError(res, err);
687
708
  }
688
709
  });
@@ -692,9 +713,14 @@ router.post('/ssh/test', async (req, res) => {
692
713
  try {
693
714
  const bc = getBrokerConfig(req);
694
715
  if (!bc.ssh || !bc.ssh.host) return res.status(400).json({ error: 'broker.ssh.host not configured' });
716
+ const user = (bc.ssh && bc.ssh.user) || require('os').userInfo().username;
717
+ const key = sshDeploy.expandHome((bc.ssh && bc.ssh.identityFile) || DEFAULT_SSH_KEY);
718
+ _log?.debug(`broker: testing SSH to ${user}@${bc.ssh.host}:${bc.ssh.port || 22} key=${key}`);
695
719
  await sshDeploy.testConnection(bc.ssh);
720
+ _log?.debug(`broker: SSH connection to ${bc.ssh.host} ok`);
696
721
  res.json({ ok: true });
697
722
  } catch (err) {
723
+ _log?.debug(`broker: SSH test to ${bc.ssh && bc.ssh.host} failed: ${err.message}`);
698
724
  res.json({ ok: false, error: err.message });
699
725
  }
700
726
  });
@@ -741,11 +767,17 @@ router.post('/wizard/bootstrap', async (req, res) => {
741
767
  const dynSecPath = `${configDir}/dynamic-security.json`;
742
768
  const confFilePath = `${configDir}/mosquitto.conf`;
743
769
 
770
+ _log?.debug(`broker: wizard bootstrap mode=${isRemote ? 'remote' : 'local'} configDir=${configDir} adminUser=${username}`);
771
+
744
772
  if (isRemote) {
745
773
  // mosquitto_ctrl must run on the broker host — invoke it via SSH.
774
+ const ctrlCmd = `mosquitto_ctrl dynsec init "${dynSecPath}" "${username}" "${password}"`;
775
+ _log?.debug(`broker: SSH mosquitto_ctrl on ${bc.ssh.host}: mosquitto_ctrl dynsec init "${dynSecPath}" "${username}" ***`);
746
776
  try {
747
- await sshDeploy.runCommand(bc.ssh, `mosquitto_ctrl dynsec init "${dynSecPath}" "${username}" "${password}"`);
777
+ const r = await sshDeploy.runCommand(bc.ssh, ctrlCmd);
778
+ _log?.debug(`broker: mosquitto_ctrl ok stdout=${r.stdout} stderr=${r.stderr}`);
748
779
  } catch (err) {
780
+ _log?.debug(`broker: mosquitto_ctrl SSH failed: ${err.message}`);
749
781
  return res.status(500).json({
750
782
  error: `mosquitto_ctrl failed on remote host: ${err.message}. Ensure mosquitto is installed on the remote broker host.`,
751
783
  });
@@ -754,23 +786,32 @@ router.post('/wizard/bootstrap', async (req, res) => {
754
786
  // Read the remote mosquitto.conf, parse, and add the plugin line if missing.
755
787
  let remoteConfRaw = '';
756
788
  try {
789
+ _log?.debug(`broker: reading remote conf ${bc.ssh.host}:${confFilePath}`);
757
790
  remoteConfRaw = await sshDeploy.readRemoteFile(bc.ssh, confFilePath);
758
- } catch {
759
- // File may not exist yet — start from an empty config
791
+ _log?.debug(`broker: remote conf read ok (${remoteConfRaw.length} bytes)`);
792
+ } catch (e) {
793
+ _log?.debug(`broker: remote conf read failed (${e.message}), starting from empty config`);
760
794
  }
761
795
  const parsed = mosquittoConf.parseText(remoteConfRaw);
762
796
  if (!parsed.managed.plugin || !String(parsed.managed.plugin).includes('mosquitto_dynamic_security')) {
763
797
  parsed.managed.plugin = 'mosquitto_dynamic_security.so';
764
798
  parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
765
799
  const content = mosquittoConf.serialise(parsed);
800
+ _log?.debug(`broker: uploading updated conf to ${bc.ssh.host}:${confFilePath}`);
766
801
  await sshDeploy.uploadContent(bc.ssh, content, confFilePath);
802
+ _log?.debug('broker: conf upload ok');
803
+ } else {
804
+ _log?.debug('broker: plugin line already present in remote conf, skipping upload');
767
805
  }
768
806
  } else {
769
807
  // Local mode: run mosquitto_ctrl on this host.
770
808
  fs.mkdirSync(configDir, { recursive: true });
809
+ _log?.debug(`broker: local mosquitto_ctrl dynsec init ${dynSecPath} ${username} ***`);
771
810
  try {
772
- await execFileAsync('mosquitto_ctrl', ['dynsec', 'init', dynSecPath, username, password], { timeout: 10000 });
811
+ const r = await execFileAsync('mosquitto_ctrl', ['dynsec', 'init', dynSecPath, username, password], { timeout: 10000 });
812
+ _log?.debug(`broker: mosquitto_ctrl ok stdout=${r.stdout} stderr=${r.stderr}`);
773
813
  } catch (err) {
814
+ _log?.debug(`broker: local mosquitto_ctrl failed: ${err.message}`);
774
815
  return res.status(500).json({
775
816
  error: `mosquitto_ctrl failed: ${err.message}. Ensure mosquitto is installed on this host.`,
776
817
  });
@@ -782,7 +823,11 @@ router.post('/wizard/bootstrap', async (req, res) => {
782
823
  parsed.managed.plugin = 'mosquitto_dynamic_security.so';
783
824
  parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
784
825
  const content = mosquittoConf.serialise(parsed);
826
+ _log?.debug(`broker: writing updated local conf to ${confFilePath}`);
785
827
  mosquittoConf.write(confFilePath, content);
828
+ _log?.debug('broker: local conf write ok');
829
+ } else {
830
+ _log?.debug('broker: plugin line already present in local conf, skipping write');
786
831
  }
787
832
  }
788
833