tmux-watch 2026.2.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.
- package/LICENSE +21 -0
- package/README.md +256 -0
- package/index.ts +29 -0
- package/openclaw.plugin.json +47 -0
- package/package.json +47 -0
- package/skills/SKILL.md +124 -0
- package/src/cli.ts +120 -0
- package/src/config.ts +129 -0
- package/src/manager.ts +837 -0
- package/src/service.ts +17 -0
- package/src/tmux-watch-tool.ts +189 -0
- package/types/openclaw-plugin-sdk.d.ts +71 -0
package/src/config.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
export type NotifyMode = "last" | "targets" | "targets+last";
|
|
2
|
+
|
|
3
|
+
export type NotifyTarget = {
|
|
4
|
+
channel: string;
|
|
5
|
+
target: string;
|
|
6
|
+
accountId?: string;
|
|
7
|
+
threadId?: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type TmuxWatchConfig = {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
captureIntervalSeconds?: number;
|
|
14
|
+
pollIntervalMs?: number;
|
|
15
|
+
stableCount?: number;
|
|
16
|
+
stableSeconds?: number;
|
|
17
|
+
captureLines: number;
|
|
18
|
+
stripAnsi: boolean;
|
|
19
|
+
maxOutputChars: number;
|
|
20
|
+
sessionKey?: string;
|
|
21
|
+
socket?: string;
|
|
22
|
+
notify: {
|
|
23
|
+
mode: NotifyMode;
|
|
24
|
+
targets: NotifyTarget[];
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_CAPTURE_INTERVAL_SECONDS = 10;
|
|
29
|
+
export const DEFAULT_STABLE_COUNT = 6;
|
|
30
|
+
|
|
31
|
+
const DEFAULTS: Omit<TmuxWatchConfig, "captureIntervalSeconds" | "pollIntervalMs" | "stableCount" | "stableSeconds"> = {
|
|
32
|
+
enabled: true,
|
|
33
|
+
captureLines: 200,
|
|
34
|
+
stripAnsi: true,
|
|
35
|
+
maxOutputChars: 4000,
|
|
36
|
+
sessionKey: undefined,
|
|
37
|
+
socket: undefined,
|
|
38
|
+
notify: {
|
|
39
|
+
mode: "last",
|
|
40
|
+
targets: [],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
45
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function readNumber(raw: unknown, fallback: number): number {
|
|
49
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
50
|
+
return raw;
|
|
51
|
+
}
|
|
52
|
+
return fallback;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function readOptionalNumber(raw: unknown): number | undefined {
|
|
56
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
57
|
+
return raw;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function readBoolean(raw: unknown, fallback: boolean): boolean {
|
|
63
|
+
return typeof raw === "boolean" ? raw : fallback;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readString(raw: unknown): string | undefined {
|
|
67
|
+
if (typeof raw !== "string") {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const trimmed = raw.trim();
|
|
71
|
+
return trimmed ? trimmed : undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeNotifyMode(raw: unknown, fallback: NotifyMode): NotifyMode {
|
|
75
|
+
if (raw === "last" || raw === "targets" || raw === "targets+last") {
|
|
76
|
+
return raw;
|
|
77
|
+
}
|
|
78
|
+
return fallback;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function normalizeTargets(raw: unknown): NotifyTarget[] {
|
|
82
|
+
if (!Array.isArray(raw)) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
const targets: NotifyTarget[] = [];
|
|
86
|
+
for (const entry of raw) {
|
|
87
|
+
if (!isRecord(entry)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const channel = readString(entry.channel);
|
|
91
|
+
const target = readString(entry.target);
|
|
92
|
+
if (!channel || !target) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
targets.push({
|
|
96
|
+
channel,
|
|
97
|
+
target,
|
|
98
|
+
accountId: readString(entry.accountId),
|
|
99
|
+
threadId: readString(entry.threadId),
|
|
100
|
+
label: readString(entry.label),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return targets;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function resolveTmuxWatchConfig(raw: unknown): TmuxWatchConfig {
|
|
107
|
+
const value = isRecord(raw) ? raw : {};
|
|
108
|
+
const notifyRaw = isRecord(value.notify) ? value.notify : {};
|
|
109
|
+
|
|
110
|
+
const captureLines = Math.max(10, readNumber(value.captureLines, DEFAULTS.captureLines));
|
|
111
|
+
const maxOutputChars = Math.max(200, readNumber(value.maxOutputChars, DEFAULTS.maxOutputChars));
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
enabled: readBoolean(value.enabled, DEFAULTS.enabled),
|
|
115
|
+
captureIntervalSeconds: readOptionalNumber(value.captureIntervalSeconds),
|
|
116
|
+
pollIntervalMs: readOptionalNumber(value.pollIntervalMs),
|
|
117
|
+
stableCount: readOptionalNumber(value.stableCount),
|
|
118
|
+
stableSeconds: readOptionalNumber(value.stableSeconds),
|
|
119
|
+
captureLines,
|
|
120
|
+
stripAnsi: readBoolean(value.stripAnsi, DEFAULTS.stripAnsi),
|
|
121
|
+
maxOutputChars,
|
|
122
|
+
sessionKey: readString(value.sessionKey) ?? DEFAULTS.sessionKey,
|
|
123
|
+
socket: readString(value.socket) ?? DEFAULTS.socket,
|
|
124
|
+
notify: {
|
|
125
|
+
mode: normalizeNotifyMode(notifyRaw.mode, DEFAULTS.notify.mode),
|
|
126
|
+
targets: normalizeTargets(notifyRaw.targets),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|