smart-home-engine 1.1.4 → 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-DmNh4UvK.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,10 +172,10 @@
172
172
  }
173
173
  })();
174
174
  </script>
175
- <script type="module" crossorigin src="/assets/index-DmNh4UvK.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
- <link rel="stylesheet" crossorigin href="/assets/index-DKIgEFlE.css">
178
+ <link rel="stylesheet" crossorigin href="/assets/index-iTrR9H4f.css">
179
179
  </head>
180
180
  <body>
181
181
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-home-engine",
3
- "version": "1.1.4",
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
  });
@@ -210,12 +223,18 @@ router.post('/restart', async (req, res) => {
210
223
  try {
211
224
  const bc = getBrokerConfig(req);
212
225
  if (bc.ssh && bc.ssh.host) {
226
+ const cmd = bc.restartCmd || 'sudo systemctl restart mosquitto';
227
+ _log?.debug(`broker: remote restart on ${bc.ssh.host}: ${cmd}`);
213
228
  const result = await sshDeploy.runCommand(bc.ssh, cmd);
229
+ _log?.debug(`broker: remote restart stdout=${result.stdout} stderr=${result.stderr}`);
214
230
  return res.json({ ok: true, ...result });
215
231
  }
232
+ _log?.debug('broker: local restart mosquitto');
216
233
  const result = await mosquittoConf.restart(bc);
234
+ _log?.debug(`broker: local restart stdout=${result.stdout} stderr=${result.stderr}`);
217
235
  res.json({ ok: true, stdout: result.stdout, stderr: result.stderr });
218
236
  } catch (err) {
237
+ _log?.debug(`broker: restart error: ${err.message}`);
219
238
  handleError(res, err);
220
239
  }
221
240
  });
@@ -654,7 +673,7 @@ router.delete('/ca/trusted/:fingerprint', async (req, res) => {
654
673
  }
655
674
  });
656
675
 
657
- module.exports = { router };
676
+ module.exports = { router, setLogger };
658
677
 
659
678
  // ── SSH routes ─────────────────────────────────────────────────────────────────
660
679
  // Note: these routes are mounted on the same router but defined after module.exports
@@ -679,9 +698,12 @@ router.post('/ssh/keygen', async (req, res) => {
679
698
  try {
680
699
  const bc = getBrokerConfig(req);
681
700
  const identityFile = (bc.ssh && bc.ssh.identityFile) || DEFAULT_SSH_KEY;
701
+ _log?.debug(`broker: generating SSH keypair at ${identityFile}`);
682
702
  const publicKey = await sshDeploy.generateKeypair(identityFile);
703
+ _log?.debug('broker: SSH keypair generated ok');
683
704
  res.json({ ok: true, publicKey });
684
705
  } catch (err) {
706
+ _log?.debug(`broker: SSH keygen error: ${err.message}`);
685
707
  handleError(res, err);
686
708
  }
687
709
  });
@@ -691,9 +713,14 @@ router.post('/ssh/test', async (req, res) => {
691
713
  try {
692
714
  const bc = getBrokerConfig(req);
693
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}`);
694
719
  await sshDeploy.testConnection(bc.ssh);
720
+ _log?.debug(`broker: SSH connection to ${bc.ssh.host} ok`);
695
721
  res.json({ ok: true });
696
722
  } catch (err) {
723
+ _log?.debug(`broker: SSH test to ${bc.ssh && bc.ssh.host} failed: ${err.message}`);
697
724
  res.json({ ok: false, error: err.message });
698
725
  }
699
726
  });
@@ -740,11 +767,17 @@ router.post('/wizard/bootstrap', async (req, res) => {
740
767
  const dynSecPath = `${configDir}/dynamic-security.json`;
741
768
  const confFilePath = `${configDir}/mosquitto.conf`;
742
769
 
770
+ _log?.debug(`broker: wizard bootstrap mode=${isRemote ? 'remote' : 'local'} configDir=${configDir} adminUser=${username}`);
771
+
743
772
  if (isRemote) {
744
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}" ***`);
745
776
  try {
746
- 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}`);
747
779
  } catch (err) {
780
+ _log?.debug(`broker: mosquitto_ctrl SSH failed: ${err.message}`);
748
781
  return res.status(500).json({
749
782
  error: `mosquitto_ctrl failed on remote host: ${err.message}. Ensure mosquitto is installed on the remote broker host.`,
750
783
  });
@@ -753,23 +786,32 @@ router.post('/wizard/bootstrap', async (req, res) => {
753
786
  // Read the remote mosquitto.conf, parse, and add the plugin line if missing.
754
787
  let remoteConfRaw = '';
755
788
  try {
789
+ _log?.debug(`broker: reading remote conf ${bc.ssh.host}:${confFilePath}`);
756
790
  remoteConfRaw = await sshDeploy.readRemoteFile(bc.ssh, confFilePath);
757
- } catch {
758
- // 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`);
759
794
  }
760
795
  const parsed = mosquittoConf.parseText(remoteConfRaw);
761
796
  if (!parsed.managed.plugin || !String(parsed.managed.plugin).includes('mosquitto_dynamic_security')) {
762
797
  parsed.managed.plugin = 'mosquitto_dynamic_security.so';
763
798
  parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
764
799
  const content = mosquittoConf.serialise(parsed);
800
+ _log?.debug(`broker: uploading updated conf to ${bc.ssh.host}:${confFilePath}`);
765
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');
766
805
  }
767
806
  } else {
768
807
  // Local mode: run mosquitto_ctrl on this host.
769
808
  fs.mkdirSync(configDir, { recursive: true });
809
+ _log?.debug(`broker: local mosquitto_ctrl dynsec init ${dynSecPath} ${username} ***`);
770
810
  try {
771
- 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}`);
772
813
  } catch (err) {
814
+ _log?.debug(`broker: local mosquitto_ctrl failed: ${err.message}`);
773
815
  return res.status(500).json({
774
816
  error: `mosquitto_ctrl failed: ${err.message}. Ensure mosquitto is installed on this host.`,
775
817
  });
@@ -781,7 +823,11 @@ router.post('/wizard/bootstrap', async (req, res) => {
781
823
  parsed.managed.plugin = 'mosquitto_dynamic_security.so';
782
824
  parsed.managed.plugin_opt_dynsec_config_file = dynSecPath;
783
825
  const content = mosquittoConf.serialise(parsed);
826
+ _log?.debug(`broker: writing updated local conf to ${confFilePath}`);
784
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');
785
831
  }
786
832
  }
787
833