@rubytech/taskmaster 1.22.0 → 1.22.2
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.
- package/dist/build-info.json +3 -3
- package/dist/config/validation.js +8 -8
- package/dist/control-ui/assets/{index-BErknDpG.js → index-CT2I2Boa.js} +3 -3
- package/dist/control-ui/assets/index-CT2I2Boa.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/logging/logger.js +35 -4
- package/dist/plugins/discovery.js +2 -2
- package/dist/web/auto-reply/monitor.js +48 -7
- package/package.json +1 -1
- package/dist/control-ui/assets/index-BErknDpG.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Taskmaster Control</title>
|
|
7
7
|
<meta name="color-scheme" content="dark light" />
|
|
8
8
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-CT2I2Boa.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-C7ieCeTV.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
package/dist/logging/logger.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { Logger as TsLogger } from "tslog";
|
|
5
6
|
import { levelToMinLevel, normalizeLogLevel } from "./levels.js";
|
|
@@ -66,8 +67,34 @@ export function isFileLogLevelEnabled(level) {
|
|
|
66
67
|
return false;
|
|
67
68
|
return levelToMinLevel(level) <= levelToMinLevel(settings.level);
|
|
68
69
|
}
|
|
70
|
+
function ensureWritableLogDir(dir) {
|
|
71
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
72
|
+
try {
|
|
73
|
+
fs.accessSync(dir, fs.constants.W_OK);
|
|
74
|
+
return dir;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Directory exists but is not writable (e.g. created by root, daemon runs as non-root).
|
|
78
|
+
// Try to fix permissions.
|
|
79
|
+
try {
|
|
80
|
+
fs.chmodSync(dir, 0o1777);
|
|
81
|
+
fs.accessSync(dir, fs.constants.W_OK);
|
|
82
|
+
return dir;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// chmod also failed — fall back to ~/.taskmaster/logs/
|
|
86
|
+
const fallback = path.join(os.homedir(), ".taskmaster", "logs");
|
|
87
|
+
fs.mkdirSync(fallback, { recursive: true });
|
|
88
|
+
process.stderr.write(`[taskmaster] log dir ${dir} is not writable, falling back to ${fallback}\n`);
|
|
89
|
+
return fallback;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
69
93
|
function buildLogger(settings) {
|
|
70
|
-
|
|
94
|
+
const logDir = ensureWritableLogDir(path.dirname(settings.file));
|
|
95
|
+
if (logDir !== path.dirname(settings.file)) {
|
|
96
|
+
settings = { ...settings, file: path.join(logDir, path.basename(settings.file)) };
|
|
97
|
+
}
|
|
71
98
|
// Clean up stale rolling logs when using a dated log filename.
|
|
72
99
|
if (isRollingPath(settings.file)) {
|
|
73
100
|
pruneOldRollingLogs(path.dirname(settings.file));
|
|
@@ -81,9 +108,10 @@ function buildLogger(settings) {
|
|
|
81
108
|
// rollover targets today's file — even for child loggers cached by
|
|
82
109
|
// subsystem loggers that outlive a parent logger rebuild.
|
|
83
110
|
const fixedFile = isRollingPath(settings.file) ? null : settings.file;
|
|
111
|
+
const resolvedLogDir = path.dirname(settings.file);
|
|
84
112
|
logger.attachTransport((logObj) => {
|
|
85
113
|
try {
|
|
86
|
-
const file = fixedFile ??
|
|
114
|
+
const file = fixedFile ?? rollingPathForToday(resolvedLogDir);
|
|
87
115
|
const time = logObj.date?.toISOString?.() ?? new Date().toISOString();
|
|
88
116
|
const line = JSON.stringify({ ...logObj, time });
|
|
89
117
|
fs.appendFileSync(file, `${line}\n`, { encoding: "utf8" });
|
|
@@ -165,9 +193,12 @@ function formatLocalDate(date) {
|
|
|
165
193
|
const day = String(date.getDate()).padStart(2, "0");
|
|
166
194
|
return `${year}-${month}-${day}`;
|
|
167
195
|
}
|
|
168
|
-
function
|
|
196
|
+
function rollingPathForToday(dir) {
|
|
169
197
|
const today = formatLocalDate(new Date());
|
|
170
|
-
return path.join(
|
|
198
|
+
return path.join(dir, `${LOG_PREFIX}-${today}${LOG_SUFFIX}`);
|
|
199
|
+
}
|
|
200
|
+
function defaultRollingPathForToday() {
|
|
201
|
+
return rollingPathForToday(DEFAULT_LOG_DIR);
|
|
171
202
|
}
|
|
172
203
|
function isRollingPath(file) {
|
|
173
204
|
const base = path.basename(file);
|
|
@@ -274,8 +274,8 @@ export function discoverTaskmasterPlugins(params) {
|
|
|
274
274
|
}
|
|
275
275
|
else {
|
|
276
276
|
diagnostics.push({
|
|
277
|
-
level: "
|
|
278
|
-
message: `bundled extensions dir not found at ${bundled.resolvedPath ?? "unknown"}`,
|
|
277
|
+
level: "warn",
|
|
278
|
+
message: `bundled extensions dir not found at ${bundled.resolvedPath ?? "unknown"} (source: ${bundled.source ?? "package-relative"})`,
|
|
279
279
|
source: "bundled-dir",
|
|
280
280
|
});
|
|
281
281
|
}
|
|
@@ -23,6 +23,52 @@ import { buildMentionConfig } from "./mentions.js";
|
|
|
23
23
|
import { createEchoTracker } from "./monitor/echo.js";
|
|
24
24
|
import { createWebOnMessageHandler } from "./monitor/on-message.js";
|
|
25
25
|
import { isLikelyWhatsAppCryptoError } from "./util.js";
|
|
26
|
+
/**
|
|
27
|
+
* Translate raw Baileys error strings into messages a non-technical user can act on.
|
|
28
|
+
* The raw error is still logged to the system log file for debugging.
|
|
29
|
+
*/
|
|
30
|
+
function humanizeWhatsAppError(statusCode, rawError) {
|
|
31
|
+
const lower = rawError.toLowerCase();
|
|
32
|
+
// Conflict: another WhatsApp Web session took over (credentials still valid)
|
|
33
|
+
if (/conflict/i.test(rawError)) {
|
|
34
|
+
return "WhatsApp was opened on another device or browser. Reconnecting automatically…";
|
|
35
|
+
}
|
|
36
|
+
// True logout — session credentials are no longer valid
|
|
37
|
+
if (statusCode === 401) {
|
|
38
|
+
return "WhatsApp session expired or was removed from the phone. Open the setup page and re-link WhatsApp.";
|
|
39
|
+
}
|
|
40
|
+
// Connection lost / timed out (408)
|
|
41
|
+
if (statusCode === 408 || lower.includes("timed out") || lower.includes("connection was lost")) {
|
|
42
|
+
return "Connection to WhatsApp lost. Reconnecting automatically…";
|
|
43
|
+
}
|
|
44
|
+
// Connection closed by server (428)
|
|
45
|
+
if (statusCode === 428 ||
|
|
46
|
+
lower.includes("connection closed") ||
|
|
47
|
+
lower.includes("connection terminated")) {
|
|
48
|
+
return "WhatsApp connection closed unexpectedly. Reconnecting automatically…";
|
|
49
|
+
}
|
|
50
|
+
// Connection replaced (440) — another session, similar to conflict
|
|
51
|
+
if (statusCode === 440) {
|
|
52
|
+
return "WhatsApp was opened on another device. Reconnecting automatically…";
|
|
53
|
+
}
|
|
54
|
+
// Bad session data (500)
|
|
55
|
+
if (statusCode === 500 || lower.includes("bad session")) {
|
|
56
|
+
return "WhatsApp session data may be corrupted. Try re-linking WhatsApp from the setup page.";
|
|
57
|
+
}
|
|
58
|
+
// Restart required (515)
|
|
59
|
+
if (statusCode === 515) {
|
|
60
|
+
return "WhatsApp requires a reconnection. Reconnecting automatically…";
|
|
61
|
+
}
|
|
62
|
+
// Forbidden (403)
|
|
63
|
+
if (statusCode === 403) {
|
|
64
|
+
return "WhatsApp rejected the connection. The phone number may be banned or restricted.";
|
|
65
|
+
}
|
|
66
|
+
// Fallback: strip technical prefixes but keep the core message
|
|
67
|
+
return (rawError
|
|
68
|
+
.replace(/^(Boom:|Error:)\s*/i, "")
|
|
69
|
+
.replace(/\s*\{.*\}\s*$/, "")
|
|
70
|
+
.trim() || "WhatsApp disconnected unexpectedly. Reconnecting…");
|
|
71
|
+
}
|
|
26
72
|
export async function monitorWebChannel(verbose, listenerFactory = monitorWebInbox, keepAlive = true, replyResolver = getReplyFromConfig, runtime = defaultRuntime, abortSignal, tuning = {}) {
|
|
27
73
|
const runId = newConnectionId();
|
|
28
74
|
const replyLogger = getChildLogger({ module: "web-auto-reply", runId });
|
|
@@ -173,9 +219,7 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
173
219
|
// carry status 401, but it is NOT a true logout — credentials remain valid.
|
|
174
220
|
const isConflict = /conflict/i.test(errorStr);
|
|
175
221
|
const loggedOut = statusCode === 401 && !isConflict;
|
|
176
|
-
const userError =
|
|
177
|
-
? "WhatsApp was opened on another device or browser. Reconnecting automatically…"
|
|
178
|
-
: errorStr;
|
|
222
|
+
const userError = humanizeWhatsAppError(statusCode, errorStr);
|
|
179
223
|
status.connected = false;
|
|
180
224
|
status.lastEventAt = Date.now();
|
|
181
225
|
status.lastDisconnect = {
|
|
@@ -355,10 +399,7 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
355
399
|
"isLoggedOut" in reason &&
|
|
356
400
|
reason.isLoggedOut;
|
|
357
401
|
const errorStr = formatError(reason);
|
|
358
|
-
const
|
|
359
|
-
const userError = isConflict
|
|
360
|
-
? "WhatsApp was opened on another device or browser. Reconnecting automatically…"
|
|
361
|
-
: errorStr;
|
|
402
|
+
const userError = humanizeWhatsAppError(statusCode, errorStr);
|
|
362
403
|
status.connected = false;
|
|
363
404
|
status.lastEventAt = Date.now();
|
|
364
405
|
status.lastDisconnect = {
|