smart-home-engine 1.14.3 → 1.15.0

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-Dt8a9eeo.js";import{t as I}from"./index-BwN-1kYF.js";/*!-----------------------------------------------------------------------------
1
+ import{m as O}from"./monaco-langs-Dt8a9eeo.js";import{t as I}from"./index-DfHNSs-x.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-BwN-1kYF.js"></script>
175
+ <script type="module" crossorigin src="/assets/index-DfHNSs-x.js"></script>
176
176
  <link rel="modulepreload" crossorigin href="/assets/monaco-langs-Dt8a9eeo.js">
177
177
  <link rel="stylesheet" crossorigin href="/assets/monaco-langs-DyX1CsEw.css">
178
- <link rel="stylesheet" crossorigin href="/assets/index-BXB4YlVH.css">
178
+ <link rel="stylesheet" crossorigin href="/assets/index-LTpStzqg.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.14.3",
3
+ "version": "1.15.0",
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
@@ -37,6 +37,26 @@ if (process.argv.includes('--install')) {
37
37
  // Ensure the data directory exists before anything else runs
38
38
  require('./lib/storage').ensureRoot();
39
39
 
40
+ // ---------------------------------------------------------------------------
41
+ // Persistent JSON-Lines log file — written alongside the pino-pretty stream.
42
+ // On each daemon start: rotate she.jsonl → she.jsonl.1, then open fresh.
43
+ // ---------------------------------------------------------------------------
44
+ const _fs = require('fs');
45
+ const _path = require('path');
46
+ const { LOGS_DIR } = require('./lib/storage');
47
+ const _logFileCurrent = _path.join(LOGS_DIR, 'she.jsonl');
48
+ const _logFilePrev = _path.join(LOGS_DIR, 'she.jsonl.1');
49
+ try {
50
+ _fs.renameSync(_logFileCurrent, _logFilePrev);
51
+ } catch {
52
+ /* no previous file — ignore */
53
+ }
54
+ const _logFileStream = _fs.createWriteStream(_logFileCurrent, { flags: 'w' });
55
+ function _writeLogLine(level, msg) {
56
+ _logFileStream.write(JSON.stringify({ level, msg, ts: Date.now() }) + '\n');
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+
40
60
  const config = require('./config');
41
61
 
42
62
  // Apply configured timezone before any Date/scheduler usage
@@ -60,18 +80,22 @@ const log = {
60
80
  debug: (...args) => {
61
81
  _pino.debug(args.join(' '));
62
82
  broadcastLog({ level: 'debug', msg: args.join(' '), ts: Date.now() });
83
+ _writeLogLine('debug', args.join(' '));
63
84
  },
64
85
  info: (...args) => {
65
86
  _pino.info(args.join(' '));
66
87
  broadcastLog({ level: 'info', msg: args.join(' '), ts: Date.now() });
88
+ _writeLogLine('info', args.join(' '));
67
89
  },
68
90
  warn: (...args) => {
69
91
  _pino.warn(args.join(' '));
70
92
  broadcastLog({ level: 'warn', msg: args.join(' '), ts: Date.now() });
93
+ _writeLogLine('warn', args.join(' '));
71
94
  },
72
95
  error: (...args) => {
73
96
  _pino.error(args.join(' '));
74
97
  broadcastLog({ level: 'error', msg: args.join(' '), ts: Date.now() });
98
+ _writeLogLine('error', args.join(' '));
75
99
  },
76
100
  setLevel: (level) => {
77
101
  _pino.level = level;
@@ -8,6 +8,7 @@ const STORAGE_ROOT = process.env.SHE_DATA_DIR || path.join(os.homedir(), '.she')
8
8
  const CONFIG_ROOT = path.join(STORAGE_ROOT, 'config');
9
9
  const SCRIPTS_ROOT = path.join(STORAGE_ROOT, 'scripts');
10
10
  const DB_ROOT = path.join(STORAGE_ROOT, 'db');
11
+ const LOGS_DIR = path.join(STORAGE_ROOT, 'logs');
11
12
 
12
13
  /**
13
14
  * Return the absolute path for a named sub-directory of ~/.she/.
@@ -43,6 +44,7 @@ function ensureRoot() {
43
44
  fs.mkdirSync(CONFIG_ROOT, { recursive: true });
44
45
  fs.mkdirSync(SCRIPTS_ROOT, { recursive: true });
45
46
  fs.mkdirSync(DB_ROOT, { recursive: true });
47
+ fs.mkdirSync(LOGS_DIR, { recursive: true });
46
48
  }
47
49
 
48
50
  /**
@@ -71,4 +73,4 @@ function ensureUserPackageJson() {
71
73
  return pkgPath;
72
74
  }
73
75
 
74
- module.exports = { STORAGE_ROOT, CONFIG_ROOT, SCRIPTS_ROOT, DB_ROOT, getStoragePath, getConfigPath, ensureStorageDir, ensureRoot, ensureUserPackageJson };
76
+ module.exports = { STORAGE_ROOT, CONFIG_ROOT, SCRIPTS_ROOT, DB_ROOT, LOGS_DIR, getStoragePath, getConfigPath, ensureStorageDir, ensureRoot, ensureUserPackageJson };
@@ -317,4 +317,4 @@ router.use((req, res) => {
317
317
  res.status(405).json({ error: 'Method not allowed' });
318
318
  });
319
319
 
320
- module.exports = { router };
320
+ module.exports = { router, safePath };
package/src/web/server.js CHANGED
@@ -124,6 +124,31 @@ app.get('/she/status', (req, res) => {
124
124
  res.json(s);
125
125
  });
126
126
 
127
+ // Log history from the current daemon run's she.jsonl file.
128
+ // ?since=<ts> filters to entries with ts >= that timestamp (default: 0 = all).
129
+ app.get('/she/logs/history', (req, res) => {
130
+ const since = req.query.since ? parseInt(req.query.since, 10) : 0;
131
+ const { LOGS_DIR } = require('../lib/storage');
132
+ const logFile = require('path').join(LOGS_DIR, 'she.jsonl');
133
+ try {
134
+ const raw = require('fs').readFileSync(logFile, 'utf8');
135
+ const entries = raw
136
+ .split('\n')
137
+ .filter((l) => l.trim())
138
+ .map((l) => {
139
+ try {
140
+ return JSON.parse(l);
141
+ } catch {
142
+ return null;
143
+ }
144
+ })
145
+ .filter((e) => e && typeof e.ts === 'number' && e.ts >= since);
146
+ res.json(entries);
147
+ } catch {
148
+ res.json([]);
149
+ }
150
+ });
151
+
127
152
  // Serve the built Svelte SPA from dist/web/
128
153
  // Hashed assets (JS/CSS) are immutable; index.html must never be cached so
129
154
  // browsers always pick up a freshly deployed version.
package/src/web/shedb.js CHANGED
@@ -82,6 +82,22 @@ function init({ dbPath, dbPublish, dbRetain, dbPrefix, mqttName, mqtt, log, broa
82
82
  }
83
83
  });
84
84
 
85
+ // When a view definition is saved, immediately publish the existing cached
86
+ // result if mqttpub is enabled. This covers the case where the user enables
87
+ // mqttpub on an already-computed view: the worker re-runs and finds an
88
+ // identical result, so the 'view' event is suppressed by the deepEqual
89
+ // check — without this handler nothing would be published until the next
90
+ // document change triggers a view recomputation.
91
+ _core.on('query', (id) => {
92
+ const query = _core.queries[id];
93
+ if (query && query.mqttpub && _mqtt) {
94
+ const view = _core.views[id];
95
+ if (view && !view.error) {
96
+ _mqtt.publish(_dbPrefix + 'view/' + id, JSON.stringify(view.result ?? []), { retain: query.retain === true });
97
+ }
98
+ }
99
+ });
100
+
85
101
  return _core;
86
102
  }
87
103