smart-home-engine 0.25.0 → 0.26.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.
@@ -1,4 +1,4 @@
1
- import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-DaomoeG9.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-BW2J83t5.js";import{t as I}from"./index-TjWk55_d.js";/*!-----------------------------------------------------------------------------
2
2
  * Copyright (c) Microsoft Corporation. All rights reserved.
3
3
  * Version: 0.52.2(404545bded1df6ffa41ea0af4e8ddb219018c6c1)
4
4
  * Released under the MIT license
@@ -155,10 +155,10 @@
155
155
  }
156
156
  })();
157
157
  </script>
158
- <script type="module" crossorigin src="/assets/index-DaomoeG9.js"></script>
158
+ <script type="module" crossorigin src="/assets/index-TjWk55_d.js"></script>
159
159
  <link rel="modulepreload" crossorigin href="/assets/monaco-langs-BW2J83t5.js">
160
160
  <link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
161
- <link rel="stylesheet" crossorigin href="/assets/index-2XghiaRb.css">
161
+ <link rel="stylesheet" crossorigin href="/assets/index-CND95LL-.css">
162
162
  </head>
163
163
  <body>
164
164
  <div id="app"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-home-engine",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
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
@@ -985,6 +985,11 @@ function runScript(script, name, origin) {
985
985
  };
986
986
 
987
987
  const scriptName = path.basename(name, path.extname(name));
988
+ // Expose read-only config values relevant to scripts.
989
+ she.config = Object.freeze({
990
+ latitude: config.latitude,
991
+ longitude: config.longitude,
992
+ });
988
993
  // she.setTimeout / she.clearTimeout — tracked versions for use by stdlib and
989
994
  // sandbox modules that don't have direct access to the Sandbox context.
990
995
  she.setTimeout = (fn, delay, ...args) => {
@@ -1190,20 +1195,6 @@ function loadSandbox(callback) {
1190
1195
  }
1191
1196
  });
1192
1197
 
1193
- if (!config.disableWatch) {
1194
- const sandboxWatcher = chokidar.watch(dir, {
1195
- ignored: (p, stats) => stats?.isFile() && !p.endsWith('.js'),
1196
- persistent: true,
1197
- ignoreInitial: true,
1198
- usePolling: true,
1199
- });
1200
- sandboxWatcher.on('ready', () => log.debug('watch', dir, 'initialized'));
1201
- sandboxWatcher.on('all', (event, filePath) => {
1202
- sandboxWatcher.close();
1203
- log.info(makeLabel(filePath), 'sandbox change detected. exiting.');
1204
- process.exit(0);
1205
- });
1206
- }
1207
1198
 
1208
1199
  callback();
1209
1200
  }
@@ -7,6 +7,12 @@ const { getConfigPath } = require('../lib/storage');
7
7
 
8
8
  const DEFAULT_CONFIG_PATH = getConfigPath();
9
9
 
10
+ /**
11
+ * Config keys that are consumed only by the frontend (no daemon restart needed).
12
+ * All other keys require a daemon restart when changed.
13
+ */
14
+ const FRONTEND_ONLY_KEYS = new Set(['gitAutoCommit', 'gitAutoPush']);
15
+
10
16
  const router = express.Router();
11
17
 
12
18
  router.get('/', (req, res) => {
@@ -23,9 +29,20 @@ router.get('/', (req, res) => {
23
29
  router.put('/', (req, res) => {
24
30
  const configPath = req.app.locals.configPath || DEFAULT_CONFIG_PATH;
25
31
  try {
32
+ let oldConfig = {};
33
+ try { oldConfig = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { /* ok — file may not exist yet */ }
34
+
26
35
  fs.mkdirSync(path.dirname(configPath), { recursive: true });
27
36
  fs.writeFileSync(configPath, JSON.stringify(req.body, null, 2), 'utf8');
28
- res.json({ ok: true, restartRequired: true, configPath });
37
+
38
+ // Restart is only required when a daemon-critical key changed.
39
+ const newConfig = req.body || {};
40
+ const allKeys = new Set([...Object.keys(oldConfig), ...Object.keys(newConfig)]);
41
+ const restartRequired = [...allKeys].some(
42
+ (k) => !FRONTEND_ONLY_KEYS.has(k) && JSON.stringify(oldConfig[k]) !== JSON.stringify(newConfig[k]),
43
+ );
44
+
45
+ res.json({ ok: true, restartRequired, configPath });
29
46
  } catch (err) {
30
47
  res.status(500).json({ error: err.message });
31
48
  }
@@ -18,7 +18,7 @@ function git(args, cwd, timeout = 30000) {
18
18
  return new Promise((resolve, reject) => {
19
19
  execFile('git', args, { cwd, timeout }, (err, stdout, stderr) => {
20
20
  if (err) {
21
- const e = new Error(stderr.trim() || err.message);
21
+ const e = new Error(stderr.trim() || stdout.trim() || err.message);
22
22
  e.stderr = stderr;
23
23
  e.stdout = stdout;
24
24
  reject(e);
@@ -198,4 +198,4 @@ router.post('/push', async (req, res) => {
198
198
  }
199
199
  });
200
200
 
201
- module.exports = { router };
201
+ module.exports = { router, git, getGitRoot };
@@ -75,6 +75,8 @@ she.global Shared mutable object across all scripts
75
75
  she.fetch(url, [opts]) HTTP/HTTPS fetch → Promise<string|object>
76
76
  Auto-parses JSON by Content-Type.
77
77
  Throws on non-2xx status.
78
+ she.config.latitude Read-only: geographic latitude from daemon config
79
+ she.config.longitude Read-only: geographic longitude from daemon config
78
80
  ```
79
81
 
80
82
  ### Script HTTP API
@@ -3,9 +3,46 @@
3
3
  const express = require('express');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const { git, getGitRoot } = require('./git-api');
6
7
 
7
8
  const router = express.Router();
8
9
 
10
+ /**
11
+ * Read gitAutoCommit / gitAutoPush from the live config file.
12
+ * Returns defaults when the file cannot be read.
13
+ */
14
+ function readGitConfig(req) {
15
+ const configPath = req.app.locals.configPath;
16
+ if (!configPath) return { autoCommit: false, autoPush: false };
17
+ try {
18
+ const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
19
+ return { autoCommit: !!cfg.gitAutoCommit, autoPush: !!cfg.gitAutoPush };
20
+ } catch {
21
+ return { autoCommit: false, autoPush: false };
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Auto-commit all changes in scriptDir if gitAutoCommit is enabled.
27
+ * Optionally pushes. Silent on errors (e.g. nothing to commit).
28
+ */
29
+ async function maybeAutoCommit(req, message) {
30
+ const { autoCommit, autoPush } = readGitConfig(req);
31
+ if (!autoCommit) return;
32
+ const scriptDir = getRoot(req);
33
+ if (!scriptDir) return;
34
+ const gitRoot = await getGitRoot(scriptDir);
35
+ if (!gitRoot) return;
36
+ try {
37
+ const scriptDirRel = path.relative(gitRoot, scriptDir).replace(/\\/g, '/');
38
+ await git(['add', scriptDirRel + '/'], gitRoot);
39
+ await git(['commit', '-m', message], gitRoot);
40
+ if (autoPush) {
41
+ try { await git(['push', 'origin'], gitRoot, 60000); } catch { /* ignore push errors */ }
42
+ }
43
+ } catch { /* nothing to commit or other transient error — ignore */ }
44
+ }
45
+
9
46
  /**
10
47
  * Resolve a safe absolute path within the script root.
11
48
  * Returns null if the resolved path escapes the root (traversal attempt).
@@ -158,6 +195,7 @@ router.use((req, res) => {
158
195
  } else {
159
196
  fs.unlinkSync(abs);
160
197
  }
198
+ maybeAutoCommit(req, `delete ${filePath}`).catch(() => {});
161
199
  return res.json({ ok: true });
162
200
  } catch (err) {
163
201
  if (err.code === 'ENOENT') return res.status(404).json({ error: 'Not found' });
@@ -194,6 +232,7 @@ router.use((req, res) => {
194
232
  try {
195
233
  fs.mkdirSync(path.dirname(absNew), { recursive: true });
196
234
  fs.renameSync(abs, absNew);
235
+ maybeAutoCommit(req, `rename ${srcPath} \u2192 ${newName}`).catch(() => {});
197
236
  return res.json({ ok: true, path: newName });
198
237
  } catch (err) {
199
238
  if (err.code === 'ENOENT') return res.status(404).json({ error: 'Not found' });