@workclaw/openclaw-workchat 1.0.0 → 1.0.4

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,147 +1,147 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
-
5
- /**
6
- * Resolve the preferred OpenClaw tmp dir.
7
- * The newer runtime exports `resolvePreferredOpenClawTmpDir` from
8
- * openclaw/plugin-sdk/temp-path; fall back to OPENCLAW_TMP_DIR / OS temp
9
- * when running against an older SDK (local typecheck, CI).
10
- */
11
- function resolvePreferredOpenClawTmpDir(): string {
12
- return (
13
- process.env.OPENCLAW_TMP_DIR?.trim()
14
- || path.join(os.tmpdir(), "openclaw")
15
- );
16
- }
17
-
18
- /**
19
- * Plugin logger — writes JSON lines to the main openclaw log file:
20
- * <tmpDir>/openclaw-YYYY-MM-DD.log
21
- * Same file and format used by all other channels.
22
- */
23
-
24
- const MAIN_LOG_DIR = resolvePreferredOpenClawTmpDir();
25
- const SUBSYSTEM = "gateway/channels/openclaw-workchat";
26
- const RUNTIME = "node";
27
- const RUNTIME_VERSION = process.versions.node;
28
- const HOSTNAME = os.hostname() || "unknown";
29
- const PARENT_NAMES = ["openclaw"];
30
-
31
- /** tslog-compatible level IDs (higher = more severe). */
32
- const LEVEL_IDS: Record<string, number> = {
33
- TRACE: 1,
34
- DEBUG: 2,
35
- INFO: 3,
36
- WARN: 4,
37
- ERROR: 5,
38
- FATAL: 6,
39
- };
40
-
41
- const DEFAULT_LOG_LEVEL = "INFO";
42
-
43
- function resolveMinLevel(): number {
44
- const env = process.env.OPENCLAW_LOG_LEVEL?.toUpperCase();
45
- if (env && env in LEVEL_IDS) return LEVEL_IDS[env];
46
- return LEVEL_IDS[DEFAULT_LOG_LEVEL];
47
- }
48
-
49
- let minLevelId = resolveMinLevel();
50
-
51
- /** Shift a Date into local time so toISOString() renders local clock digits. */
52
- function toLocalISO(now: Date): string {
53
- const offsetMs = -now.getTimezoneOffset() * 60_000;
54
- const sign = offsetMs >= 0 ? "+" : "-";
55
- const abs = Math.abs(now.getTimezoneOffset());
56
- const offStr = `${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
57
- return new Date(now.getTime() + offsetMs).toISOString().replace("Z", offStr);
58
- }
59
-
60
- function localDateKey(now: Date): string {
61
- return toLocalISO(now).slice(0, 10);
62
- }
63
-
64
- function resolveMainLogPath(): string {
65
- const dateKey = localDateKey(new Date());
66
- return path.join(MAIN_LOG_DIR, `openclaw-${dateKey}.log`);
67
- }
68
-
69
- let logDirEnsured = false;
70
-
71
- export type Logger = {
72
- info(message: string): void;
73
- debug(message: string): void;
74
- warn(message: string): void;
75
- error(message: string): void;
76
- /** Returns a child logger whose messages are prefixed with `[accountId]`. */
77
- withAccount(accountId: string): Logger;
78
- /** Returns the current main log file path. */
79
- getLogFilePath(): string;
80
- close(): void;
81
- };
82
-
83
- function buildLoggerName(accountId?: string): string {
84
- return accountId ? `${SUBSYSTEM}/${accountId}` : SUBSYSTEM;
85
- }
86
-
87
- function writeLog(level: string, message: string, accountId?: string): void {
88
- const levelId = LEVEL_IDS[level] ?? LEVEL_IDS.INFO;
89
- if (levelId < minLevelId) return;
90
-
91
- const now = new Date();
92
- const loggerName = buildLoggerName(accountId);
93
- const prefixedMessage = accountId ? `[${accountId}] ${message}` : message;
94
- const entry = JSON.stringify({
95
- "0": loggerName,
96
- "1": prefixedMessage,
97
- _meta: {
98
- runtime: RUNTIME,
99
- runtimeVersion: RUNTIME_VERSION,
100
- hostname: HOSTNAME,
101
- name: loggerName,
102
- parentNames: PARENT_NAMES,
103
- date: now.toISOString(),
104
- logLevelId: LEVEL_IDS[level] ?? LEVEL_IDS.INFO,
105
- logLevelName: level,
106
- },
107
- time: toLocalISO(now),
108
- });
109
- try {
110
- if (!logDirEnsured) {
111
- fs.mkdirSync(MAIN_LOG_DIR, { recursive: true });
112
- logDirEnsured = true;
113
- }
114
- fs.appendFileSync(resolveMainLogPath(), `${entry}\n`, "utf-8");
115
- } catch {
116
- // Best-effort; never block on logging failures.
117
- }
118
- }
119
-
120
- /** Creates a logger instance, optionally bound to a specific account. */
121
- function createLogger(accountId?: string): Logger {
122
- return {
123
- info(message: string): void {
124
- writeLog("INFO", message, accountId);
125
- },
126
- debug(message: string): void {
127
- writeLog("DEBUG", message, accountId);
128
- },
129
- warn(message: string): void {
130
- writeLog("WARN", message, accountId);
131
- },
132
- error(message: string): void {
133
- writeLog("ERROR", message, accountId);
134
- },
135
- withAccount(id: string): Logger {
136
- return createLogger(id);
137
- },
138
- getLogFilePath(): string {
139
- return resolveMainLogPath();
140
- },
141
- close(): void {
142
- // No-op: appendFileSync has no persistent handle to close.
143
- },
144
- };
145
- }
146
-
147
- export const logger: Logger = createLogger();
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * Resolve the preferred OpenClaw tmp dir.
7
+ * The newer runtime exports `resolvePreferredOpenClawTmpDir` from
8
+ * openclaw/plugin-sdk/temp-path; fall back to OPENCLAW_TMP_DIR / OS temp
9
+ * when running against an older SDK (local typecheck, CI).
10
+ */
11
+ function resolvePreferredOpenClawTmpDir(): string {
12
+ return (
13
+ process.env.OPENCLAW_TMP_DIR?.trim()
14
+ || path.join(os.tmpdir(), "openclaw")
15
+ );
16
+ }
17
+
18
+ /**
19
+ * Plugin logger — writes JSON lines to the main openclaw log file:
20
+ * <tmpDir>/openclaw-YYYY-MM-DD.log
21
+ * Same file and format used by all other channels.
22
+ */
23
+
24
+ const MAIN_LOG_DIR = resolvePreferredOpenClawTmpDir();
25
+ const SUBSYSTEM = "gateway/channels/openclaw-workchat";
26
+ const RUNTIME = "node";
27
+ const RUNTIME_VERSION = process.versions.node;
28
+ const HOSTNAME = os.hostname() || "unknown";
29
+ const PARENT_NAMES = ["openclaw"];
30
+
31
+ /** tslog-compatible level IDs (higher = more severe). */
32
+ const LEVEL_IDS: Record<string, number> = {
33
+ TRACE: 1,
34
+ DEBUG: 2,
35
+ INFO: 3,
36
+ WARN: 4,
37
+ ERROR: 5,
38
+ FATAL: 6,
39
+ };
40
+
41
+ const DEFAULT_LOG_LEVEL = "INFO";
42
+
43
+ function resolveMinLevel(): number {
44
+ const env = process.env.OPENCLAW_LOG_LEVEL?.toUpperCase();
45
+ if (env && env in LEVEL_IDS) return LEVEL_IDS[env];
46
+ return LEVEL_IDS[DEFAULT_LOG_LEVEL];
47
+ }
48
+
49
+ let minLevelId = resolveMinLevel();
50
+
51
+ /** Shift a Date into local time so toISOString() renders local clock digits. */
52
+ function toLocalISO(now: Date): string {
53
+ const offsetMs = -now.getTimezoneOffset() * 60_000;
54
+ const sign = offsetMs >= 0 ? "+" : "-";
55
+ const abs = Math.abs(now.getTimezoneOffset());
56
+ const offStr = `${sign}${String(Math.floor(abs / 60)).padStart(2, "0")}:${String(abs % 60).padStart(2, "0")}`;
57
+ return new Date(now.getTime() + offsetMs).toISOString().replace("Z", offStr);
58
+ }
59
+
60
+ function localDateKey(now: Date): string {
61
+ return toLocalISO(now).slice(0, 10);
62
+ }
63
+
64
+ function resolveMainLogPath(): string {
65
+ const dateKey = localDateKey(new Date());
66
+ return path.join(MAIN_LOG_DIR, `openclaw-${dateKey}.log`);
67
+ }
68
+
69
+ let logDirEnsured = false;
70
+
71
+ export type Logger = {
72
+ info(message: string): void;
73
+ debug(message: string): void;
74
+ warn(message: string): void;
75
+ error(message: string): void;
76
+ /** Returns a child logger whose messages are prefixed with `[accountId]`. */
77
+ withAccount(accountId: string): Logger;
78
+ /** Returns the current main log file path. */
79
+ getLogFilePath(): string;
80
+ close(): void;
81
+ };
82
+
83
+ function buildLoggerName(accountId?: string): string {
84
+ return accountId ? `${SUBSYSTEM}/${accountId}` : SUBSYSTEM;
85
+ }
86
+
87
+ function writeLog(level: string, message: string, accountId?: string): void {
88
+ const levelId = LEVEL_IDS[level] ?? LEVEL_IDS.INFO;
89
+ if (levelId < minLevelId) return;
90
+
91
+ const now = new Date();
92
+ const loggerName = buildLoggerName(accountId);
93
+ const prefixedMessage = accountId ? `[${accountId}] ${message}` : message;
94
+ const entry = JSON.stringify({
95
+ "0": loggerName,
96
+ "1": prefixedMessage,
97
+ _meta: {
98
+ runtime: RUNTIME,
99
+ runtimeVersion: RUNTIME_VERSION,
100
+ hostname: HOSTNAME,
101
+ name: loggerName,
102
+ parentNames: PARENT_NAMES,
103
+ date: now.toISOString(),
104
+ logLevelId: LEVEL_IDS[level] ?? LEVEL_IDS.INFO,
105
+ logLevelName: level,
106
+ },
107
+ time: toLocalISO(now),
108
+ });
109
+ try {
110
+ if (!logDirEnsured) {
111
+ fs.mkdirSync(MAIN_LOG_DIR, { recursive: true });
112
+ logDirEnsured = true;
113
+ }
114
+ fs.appendFileSync(resolveMainLogPath(), `${entry}\n`, "utf-8");
115
+ } catch {
116
+ // Best-effort; never block on logging failures.
117
+ }
118
+ }
119
+
120
+ /** Creates a logger instance, optionally bound to a specific account. */
121
+ function createLogger(accountId?: string): Logger {
122
+ return {
123
+ info(message: string): void {
124
+ writeLog("INFO", message, accountId);
125
+ },
126
+ debug(message: string): void {
127
+ writeLog("DEBUG", message, accountId);
128
+ },
129
+ warn(message: string): void {
130
+ writeLog("WARN", message, accountId);
131
+ },
132
+ error(message: string): void {
133
+ writeLog("ERROR", message, accountId);
134
+ },
135
+ withAccount(id: string): Logger {
136
+ return createLogger(id);
137
+ },
138
+ getLogFilePath(): string {
139
+ return resolveMainLogPath();
140
+ },
141
+ close(): void {
142
+ // No-op: appendFileSync has no persistent handle to close.
143
+ },
144
+ };
145
+ }
146
+
147
+ export const logger: Logger = createLogger();
@@ -1,9 +1,9 @@
1
- import crypto from "node:crypto";
2
-
3
- /**
4
- * Generate a prefixed unique ID using timestamp + crypto random bytes.
5
- * Format: `{prefix}:{timestamp}-{8-char hex}`
6
- */
7
- export function generateId(prefix: string): string {
8
- return `${prefix}:${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
9
- }
1
+ import crypto from "node:crypto";
2
+
3
+ /**
4
+ * Generate a prefixed unique ID using timestamp + crypto random bytes.
5
+ * Format: `{prefix}:{timestamp}-{8-char hex}`
6
+ */
7
+ export function generateId(prefix: string): string {
8
+ return `${prefix}:${Date.now()}-${crypto.randomBytes(4).toString("hex")}`;
9
+ }
@@ -1,39 +1,39 @@
1
- const DEFAULT_BODY_MAX_LEN = 200;
2
-
3
- /**
4
- * Truncate a string, appending a length indicator when trimmed.
5
- * Returns `""` for empty/undefined input.
6
- */
7
- export function truncate(s: string | undefined, max: number): string {
8
- if (!s) return "";
9
- if (s.length <= max) return s;
10
- return `${s.slice(0, max)}…(len=${s.length})`;
11
- }
12
-
13
- /**
14
- * Truncate a JSON body string to `maxLen` chars for safe logging.
15
- * Redacts known sensitive fields before truncating.
16
- */
17
- export function redactBody(body: string | undefined, maxLen = DEFAULT_BODY_MAX_LEN): string {
18
- if (!body) return "(empty)";
19
- const redacted = body.replace(
20
- /"(appSecret|app_secret|accessToken|access_token|ticket|authorization|Authorization)"\s*:\s*"[^"]*"/g,
21
- '"$1":"<redacted>"',
22
- );
23
- if (redacted.length <= maxLen) return redacted;
24
- return `${redacted.slice(0, maxLen)}…(truncated, totalLen=${redacted.length})`;
25
- }
26
-
27
- /**
28
- * Strip query string (which often contains tickets/tokens) from a URL,
29
- * keeping only origin + pathname.
30
- */
31
- export function redactUrl(rawUrl: string): string {
32
- try {
33
- const u = new URL(rawUrl);
34
- const base = `${u.origin}${u.pathname}`;
35
- return u.search ? `${base}?<redacted>` : base;
36
- } catch {
37
- return truncate(rawUrl, 80);
38
- }
39
- }
1
+ const DEFAULT_BODY_MAX_LEN = 200;
2
+
3
+ /**
4
+ * Truncate a string, appending a length indicator when trimmed.
5
+ * Returns `""` for empty/undefined input.
6
+ */
7
+ export function truncate(s: string | undefined, max: number): string {
8
+ if (!s) return "";
9
+ if (s.length <= max) return s;
10
+ return `${s.slice(0, max)}…(len=${s.length})`;
11
+ }
12
+
13
+ /**
14
+ * Truncate a JSON body string to `maxLen` chars for safe logging.
15
+ * Redacts known sensitive fields before truncating.
16
+ */
17
+ export function redactBody(body: string | undefined, maxLen = DEFAULT_BODY_MAX_LEN): string {
18
+ if (!body) return "(empty)";
19
+ const redacted = body.replace(
20
+ /"(appSecret|app_secret|accessToken|access_token|ticket|authorization|Authorization)"\s*:\s*"[^"]*"/g,
21
+ '"$1":"<redacted>"',
22
+ );
23
+ if (redacted.length <= maxLen) return redacted;
24
+ return `${redacted.slice(0, maxLen)}…(truncated, totalLen=${redacted.length})`;
25
+ }
26
+
27
+ /**
28
+ * Strip query string (which often contains tickets/tokens) from a URL,
29
+ * keeping only origin + pathname.
30
+ */
31
+ export function redactUrl(rawUrl: string): string {
32
+ try {
33
+ const u = new URL(rawUrl);
34
+ const base = `${u.origin}${u.pathname}`;
35
+ return u.search ? `${base}?<redacted>` : base;
36
+ } catch {
37
+ return truncate(rawUrl, 80);
38
+ }
39
+ }
package/src/vendor.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- declare module "qrcode-terminal" {
2
- const qrcodeTerminal: {
3
- generate(
4
- text: string,
5
- options?: { small?: boolean },
6
- callback?: (qr: string) => void,
7
- ): void;
8
- };
9
- export default qrcodeTerminal;
10
- }
1
+ declare module "qrcode-terminal" {
2
+ const qrcodeTerminal: {
3
+ generate(
4
+ text: string,
5
+ options?: { small?: boolean },
6
+ callback?: (qr: string) => void,
7
+ ): void;
8
+ };
9
+ export default qrcodeTerminal;
10
+ }