@rubytech/taskmaster 1.28.0 → 1.29.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,5 +1,5 @@
1
1
  {
2
- "version": "1.28.0",
3
- "commit": "1a2bab0b049dd3833331ea77bcdfac322348a0bb",
4
- "builtAt": "2026-03-08T05:50:10.909Z"
2
+ "version": "1.29.0",
3
+ "commit": "ae88034d2419b4e2a6cb0e359e8f685f19995455",
4
+ "builtAt": "2026-03-08T06:20:49.278Z"
5
5
  }
@@ -66,6 +66,12 @@ async function runProvision(opts) {
66
66
  console.log("[5/7] Hostname: skipped (macOS)");
67
67
  console.log("[6/7] Avahi service: skipped (macOS)");
68
68
  }
69
+ // Step 6b: Log directory (Linux only) — ensure /tmp/taskmaster is world-writable
70
+ // on every boot via systemd-tmpfiles.d so the gateway can always write logs there
71
+ // regardless of which user created the directory previously.
72
+ if (isLinux) {
73
+ await setupLogDir();
74
+ }
69
75
  // Step 7: Install daemon
70
76
  console.log("[7/7] Installing gateway daemon...");
71
77
  await runDaemonInstall({ port, force: true });
@@ -218,6 +224,24 @@ async function restartAvahi() {
218
224
  }
219
225
  }
220
226
  // ---------------------------------------------------------------------------
227
+ // Step 6b: Log directory (Linux only)
228
+ // ---------------------------------------------------------------------------
229
+ async function setupLogDir() {
230
+ const tmpfileConf = "/etc/tmpfiles.d/taskmaster.conf";
231
+ const confLine = "d /tmp/taskmaster 1777 root root -\n";
232
+ console.log("[6b/7] Log directory: writing systemd-tmpfiles.d entry...");
233
+ try {
234
+ await runCommandWithTimeout(["sudo", "sh", "-c", `echo '${confLine.trim()}' > ${tmpfileConf}`], { timeoutMs: 10_000 });
235
+ await runCommandWithTimeout(["sudo", "systemd-tmpfiles", "--create", tmpfileConf], {
236
+ timeoutMs: 10_000,
237
+ });
238
+ console.log(" /tmp/taskmaster: world-writable (1777), persists across reboots");
239
+ }
240
+ catch (err) {
241
+ console.error(` log dir setup failed: ${String(err)}`);
242
+ }
243
+ }
244
+ // ---------------------------------------------------------------------------
221
245
  // Helpers
222
246
  // ---------------------------------------------------------------------------
223
247
  async function fileExists(p) {
@@ -6,9 +6,14 @@ import { Logger as TsLogger } from "tslog";
6
6
  import { levelToMinLevel, normalizeLogLevel } from "./levels.js";
7
7
  import { readLoggingConfig } from "./config.js";
8
8
  import { loggingState } from "./state.js";
9
- // Pin to /tmp so mac Debug UI and docs match; os.tmpdir() can be a per-user
10
- // randomized path on macOS which made the “Open log” button a no-op.
11
- export const DEFAULT_LOG_DIR = "/tmp/taskmaster";
9
+ // macOS: use /tmp so the Debug UI "Open log" button points to a stable path.
10
+ // os.tmpdir() on macOS can be a per-user randomized path which made that button a no-op.
11
+ // Linux (Pi): use ~/.taskmaster/logs — /tmp uses the sticky bit so directories created
12
+ // there by a root process (e.g. an earlier daemon run) cannot be chmod'd by the user,
13
+ // making /tmp/taskmaster permanently unwritable after any accidental root creation.
14
+ export const DEFAULT_LOG_DIR = process.platform === "darwin"
15
+ ? "/tmp/taskmaster"
16
+ : path.join(os.homedir(), ".taskmaster", "logs");
12
17
  export const DEFAULT_LOG_FILE = path.join(DEFAULT_LOG_DIR, "taskmaster.log"); // legacy single-file path
13
18
  const LOG_PREFIX = "taskmaster";
14
19
  const LOG_SUFFIX = ".log";
@@ -123,15 +128,16 @@ function buildLogger(settings) {
123
128
  for (const transport of externalTransports) {
124
129
  attachExternalTransport(logger, transport);
125
130
  }
126
- return logger;
131
+ return { logger, resolved: settings };
127
132
  }
128
133
  export function getLogger() {
129
134
  const settings = resolveSettings();
130
135
  const cachedLogger = loggingState.cachedLogger;
131
136
  const cachedSettings = loggingState.cachedSettings;
132
137
  if (!cachedLogger || settingsChanged(cachedSettings, settings)) {
133
- loggingState.cachedLogger = buildLogger(settings);
134
- loggingState.cachedSettings = settings;
138
+ const { logger, resolved } = buildLogger(settings);
139
+ loggingState.cachedLogger = logger;
140
+ loggingState.cachedSettings = resolved;
135
141
  }
136
142
  return loggingState.cachedLogger;
137
143
  }
@@ -162,7 +168,10 @@ export function toPinoLikeLogger(logger, level) {
162
168
  };
163
169
  }
164
170
  export function getResolvedLoggerSettings() {
165
- return resolveSettings();
171
+ // Return cached settings when available — they reflect the actual write location,
172
+ // which may differ from resolveSettings() if ensureWritableLogDir fell back to a
173
+ // different directory (e.g. when /tmp/taskmaster is owned by root).
174
+ return loggingState.cachedSettings ?? resolveSettings();
166
175
  }
167
176
  // Test helpers
168
177
  export function setLoggerOverride(settings) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.28.0",
3
+ "version": "1.29.0",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"