openclaw-telegram-manager 2.0.2 → 2.2.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.
- package/README.md +10 -3
- package/dist/commands/archive.d.ts.map +1 -1
- package/dist/commands/archive.js +4 -6
- package/dist/commands/archive.js.map +1 -1
- package/dist/commands/autopilot.d.ts +3 -0
- package/dist/commands/autopilot.d.ts.map +1 -0
- package/dist/commands/autopilot.js +127 -0
- package/dist/commands/autopilot.js.map +1 -0
- package/dist/commands/daily-report.d.ts +4 -0
- package/dist/commands/daily-report.d.ts.map +1 -0
- package/dist/commands/daily-report.js +155 -0
- package/dist/commands/daily-report.js.map +1 -0
- package/dist/commands/doctor-all.d.ts.map +1 -1
- package/dist/commands/doctor-all.js +48 -14
- package/dist/commands/doctor-all.js.map +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +3 -2
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +0 -1
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts +6 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +73 -28
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +0 -1
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/rename.d.ts.map +1 -1
- package/dist/commands/rename.js +4 -6
- package/dist/commands/rename.js.map +1 -1
- package/dist/commands/snooze.d.ts.map +1 -1
- package/dist/commands/snooze.js +3 -5
- package/dist/commands/snooze.js.map +1 -1
- package/dist/commands/status.js +2 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +1 -4
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +4 -8
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -10
- package/dist/index.js.map +1 -1
- package/dist/lib/activity.d.ts +3 -0
- package/dist/lib/activity.d.ts.map +1 -0
- package/dist/lib/activity.js +11 -0
- package/dist/lib/activity.js.map +1 -0
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +1 -0
- package/dist/lib/auth.js.map +1 -1
- package/dist/lib/capsule.d.ts +5 -0
- package/dist/lib/capsule.d.ts.map +1 -1
- package/dist/lib/capsule.js +17 -2
- package/dist/lib/capsule.js.map +1 -1
- package/dist/lib/doctor-checks.d.ts +6 -1
- package/dist/lib/doctor-checks.d.ts.map +1 -1
- package/dist/lib/doctor-checks.js +54 -34
- package/dist/lib/doctor-checks.js.map +1 -1
- package/dist/lib/include-generator.d.ts.map +1 -1
- package/dist/lib/include-generator.js +21 -5
- package/dist/lib/include-generator.js.map +1 -1
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/lib/registry.js +18 -0
- package/dist/lib/registry.js.map +1 -1
- package/dist/lib/telegram.d.ts +25 -5
- package/dist/lib/telegram.d.ts.map +1 -1
- package/dist/lib/telegram.js +96 -46
- package/dist/lib/telegram.js.map +1 -1
- package/dist/lib/types.d.ts +8 -4
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +5 -2
- package/dist/lib/types.js.map +1 -1
- package/dist/plugin.js +1250 -714
- package/dist/setup.js +80 -4
- package/dist/setup.js.map +1 -1
- package/dist/tool.d.ts +2 -0
- package/dist/tool.d.ts.map +1 -1
- package/dist/tool.js +52 -7
- package/dist/tool.js.map +1 -1
- package/package.json +1 -1
- package/skills/tm/SKILL.md +15 -3
package/README.md
CHANGED
|
@@ -57,6 +57,8 @@ All commands are typed directly in the Telegram group chat:
|
|
|
57
57
|
| `/tm snooze <duration>` | Snooze a topic (e.g. `7d`, `30d`) |
|
|
58
58
|
| `/tm archive` | Archive a topic |
|
|
59
59
|
| `/tm unarchive` | Bring back an archived topic |
|
|
60
|
+
| `/tm autopilot [enable\|disable\|status]` | Toggle daily health sweeps |
|
|
61
|
+
| `/tm daily-report` | Generate a daily status report for the current topic |
|
|
60
62
|
| `/tm help` | Show this command list in Telegram |
|
|
61
63
|
|
|
62
64
|
## What's in a capsule
|
|
@@ -64,12 +66,13 @@ All commands are typed directly in the Telegram group chat:
|
|
|
64
66
|
Each topic gets a folder at `~/.openclaw/workspace/projects/t-<threadId>/` with these files:
|
|
65
67
|
|
|
66
68
|
**Always included:**
|
|
67
|
-
- `STATUS.md` — what's happening, last activity, next
|
|
69
|
+
- `STATUS.md` — what's happening, last activity, next actions and upcoming pipeline
|
|
68
70
|
- `TODO.md` — task backlog
|
|
69
71
|
- `COMMANDS.md` — build/deploy/test commands worth remembering
|
|
70
72
|
- `LINKS.md` — URLs and endpoints
|
|
71
73
|
- `CRON.md` — scheduled jobs
|
|
72
74
|
- `NOTES.md` — anything else worth keeping
|
|
75
|
+
- `LEARNINGS.md` — hard-won insights, mistakes, and workarounds
|
|
73
76
|
- `README.md` — what this topic is about
|
|
74
77
|
|
|
75
78
|
**Extra files by type:**
|
|
@@ -90,7 +93,7 @@ The first person to run `/tm init` automatically becomes admin.
|
|
|
90
93
|
|
|
91
94
|
- Path traversal protection (jail checks + symlink rejection)
|
|
92
95
|
- HMAC-signed inline keyboard callbacks
|
|
93
|
-
- HTML escaping on
|
|
96
|
+
- HTML escaping on direct Telegram API posts (fan-out doctor reports)
|
|
94
97
|
- Schema validation on every registry read (bad entries get quarantined)
|
|
95
98
|
- File locking to prevent concurrent write corruption
|
|
96
99
|
|
|
@@ -102,7 +105,11 @@ See [SECURITY.md](SECURITY.md) for reporting vulnerabilities.
|
|
|
102
105
|
npx openclaw-telegram-manager uninstall
|
|
103
106
|
```
|
|
104
107
|
|
|
105
|
-
This removes the plugin extension files, the `$include` reference from `openclaw.json`, and the generated include file, then restarts the gateway.
|
|
108
|
+
This removes the plugin extension files, the `$include` reference from `openclaw.json`, and the generated include file, then restarts the gateway. You will be asked whether to delete workspace data (registry, topic capsules). To skip the prompt and force deletion, pass `--purge-data`:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npx openclaw-telegram-manager uninstall --purge-data
|
|
112
|
+
```
|
|
106
113
|
|
|
107
114
|
## Contributing
|
|
108
115
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"archive.d.ts","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAE/E;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAEjF"}
|
package/dist/commands/archive.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { withRegistry, readRegistry } from '../lib/registry.js';
|
|
2
2
|
import { checkAuthorization } from '../lib/auth.js';
|
|
3
3
|
import { topicKey } from '../lib/types.js';
|
|
4
|
-
import { htmlEscape } from '../lib/security.js';
|
|
5
4
|
import { appendAudit, buildAuditEntry } from '../lib/audit.js';
|
|
6
5
|
import { generateInclude } from '../lib/include-generator.js';
|
|
7
6
|
import { triggerRestart, getConfigWrites } from '../lib/config-restart.js';
|
|
@@ -29,10 +28,10 @@ async function handleArchiveToggle(ctx, archive) {
|
|
|
29
28
|
return { text: 'This topic is not registered. Run /tm init first.' };
|
|
30
29
|
}
|
|
31
30
|
if (archive && entry.status === 'archived') {
|
|
32
|
-
return { text: `Topic
|
|
31
|
+
return { text: `Topic **${entry.name}** is already archived.` };
|
|
33
32
|
}
|
|
34
33
|
if (!archive && entry.status !== 'archived') {
|
|
35
|
-
return { text: `Topic
|
|
34
|
+
return { text: `Topic **${entry.name}** is not archived.` };
|
|
36
35
|
}
|
|
37
36
|
const newStatus = archive ? 'archived' : 'active';
|
|
38
37
|
await withRegistry(workspaceDir, (data) => {
|
|
@@ -54,7 +53,7 @@ async function handleArchiveToggle(ctx, archive) {
|
|
|
54
53
|
}
|
|
55
54
|
catch (err) {
|
|
56
55
|
const msg = err instanceof Error ? err.message : String(err);
|
|
57
|
-
restartMsg = `\nWarning: include generation failed: ${
|
|
56
|
+
restartMsg = `\nWarning: include generation failed: ${msg}`;
|
|
58
57
|
}
|
|
59
58
|
const result = await triggerRestart(rpc, logger);
|
|
60
59
|
if (!result.success && result.fallbackMessage) {
|
|
@@ -64,8 +63,7 @@ async function handleArchiveToggle(ctx, archive) {
|
|
|
64
63
|
appendAudit(workspaceDir, buildAuditEntry(userId, command, entry.slug, `Status changed to ${newStatus}`));
|
|
65
64
|
const action = archive ? 'archived' : 'unarchived';
|
|
66
65
|
return {
|
|
67
|
-
text: `Topic
|
|
68
|
-
parseMode: 'HTML',
|
|
66
|
+
text: `Topic **${entry.name}** ${action}.${restartMsg}`,
|
|
69
67
|
};
|
|
70
68
|
}
|
|
71
69
|
//# sourceMappingURL=archive.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"archive.js","sourceRoot":"","sources":["../../src/commands/archive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot.d.ts","sourceRoot":"","sources":["../../src/commands/autopilot.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAuB/D,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA0B/F"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { readRegistry, withRegistry } from '../lib/registry.js';
|
|
4
|
+
import { checkAuthorization } from '../lib/auth.js';
|
|
5
|
+
// ── Marker constants ────────────────────────────────────────────────
|
|
6
|
+
const MARKER_START = '<!-- TM_AUTOPILOT_START -->';
|
|
7
|
+
const MARKER_END = '<!-- TM_AUTOPILOT_END -->';
|
|
8
|
+
const HEARTBEAT_BLOCK = `${MARKER_START}
|
|
9
|
+
## Topic Manager — Balanced Autopilot
|
|
10
|
+
|
|
11
|
+
Execute these steps IN ORDER. Do not skip steps.
|
|
12
|
+
|
|
13
|
+
1. CALL \`topic_manager\` with command "doctor --all" to health-check all active topics.
|
|
14
|
+
This handles stale timestamps, task ID mismatches, and posting errors automatically.
|
|
15
|
+
2. For each topic where STATUS.md "Last done (UTC)" is >3 days old:
|
|
16
|
+
post a brief nudge in that topic asking the user for a status update.
|
|
17
|
+
3. If no actions were taken in steps 1-2, do nothing (HEARTBEAT_OK).
|
|
18
|
+
${MARKER_END}`;
|
|
19
|
+
const HEARTBEAT_FILENAME = 'HEARTBEAT.md';
|
|
20
|
+
// ── Main handler ────────────────────────────────────────────────────
|
|
21
|
+
export async function handleAutopilot(ctx, args) {
|
|
22
|
+
const { workspaceDir, userId } = ctx;
|
|
23
|
+
if (!userId) {
|
|
24
|
+
return { text: 'Missing context: userId not available.' };
|
|
25
|
+
}
|
|
26
|
+
const registry = readRegistry(workspaceDir);
|
|
27
|
+
const auth = checkAuthorization(userId, 'autopilot', registry);
|
|
28
|
+
if (!auth.authorized) {
|
|
29
|
+
return { text: auth.message ?? 'Not authorized.' };
|
|
30
|
+
}
|
|
31
|
+
const subCommand = args.trim().toLowerCase() || 'enable';
|
|
32
|
+
switch (subCommand) {
|
|
33
|
+
case 'enable':
|
|
34
|
+
return handleEnable(ctx);
|
|
35
|
+
case 'disable':
|
|
36
|
+
return handleDisable(ctx);
|
|
37
|
+
case 'status':
|
|
38
|
+
return handleStatus(ctx);
|
|
39
|
+
default:
|
|
40
|
+
return { text: `Unknown autopilot sub-command: "${subCommand}". Use enable, disable, or status.` };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// ── Enable ──────────────────────────────────────────────────────────
|
|
44
|
+
async function handleEnable(ctx) {
|
|
45
|
+
const { workspaceDir } = ctx;
|
|
46
|
+
const heartbeatPath = path.join(workspaceDir, HEARTBEAT_FILENAME);
|
|
47
|
+
// Read or create HEARTBEAT.md
|
|
48
|
+
let content = '';
|
|
49
|
+
try {
|
|
50
|
+
if (fs.existsSync(heartbeatPath)) {
|
|
51
|
+
content = fs.readFileSync(heartbeatPath, 'utf-8');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// File doesn't exist yet — that's fine
|
|
56
|
+
}
|
|
57
|
+
// Idempotent: don't duplicate if marker already present
|
|
58
|
+
if (content.includes(MARKER_START)) {
|
|
59
|
+
// Update registry flag anyway in case it's out of sync
|
|
60
|
+
await withRegistry(workspaceDir, (data) => {
|
|
61
|
+
data.autopilotEnabled = true;
|
|
62
|
+
});
|
|
63
|
+
return { text: 'Autopilot is already enabled.' };
|
|
64
|
+
}
|
|
65
|
+
// Append block to HEARTBEAT.md
|
|
66
|
+
const newContent = content ? content.trimEnd() + '\n\n' + HEARTBEAT_BLOCK + '\n' : HEARTBEAT_BLOCK + '\n';
|
|
67
|
+
fs.writeFileSync(heartbeatPath, newContent, { mode: 0o640 });
|
|
68
|
+
await withRegistry(workspaceDir, (data) => {
|
|
69
|
+
data.autopilotEnabled = true;
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
text: '**Autopilot enabled.**\nDaily health sweeps will run via the OpenClaw heartbeat.',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// ── Disable ─────────────────────────────────────────────────────────
|
|
76
|
+
async function handleDisable(ctx) {
|
|
77
|
+
const { workspaceDir } = ctx;
|
|
78
|
+
const heartbeatPath = path.join(workspaceDir, HEARTBEAT_FILENAME);
|
|
79
|
+
if (!fs.existsSync(heartbeatPath)) {
|
|
80
|
+
await withRegistry(workspaceDir, (data) => {
|
|
81
|
+
data.autopilotEnabled = false;
|
|
82
|
+
});
|
|
83
|
+
return { text: 'Autopilot is not enabled (no HEARTBEAT.md found).' };
|
|
84
|
+
}
|
|
85
|
+
let content = fs.readFileSync(heartbeatPath, 'utf-8');
|
|
86
|
+
if (!content.includes(MARKER_START)) {
|
|
87
|
+
await withRegistry(workspaceDir, (data) => {
|
|
88
|
+
data.autopilotEnabled = false;
|
|
89
|
+
});
|
|
90
|
+
return { text: 'Autopilot is not enabled (no marker found in HEARTBEAT.md).' };
|
|
91
|
+
}
|
|
92
|
+
// Remove everything between markers (inclusive)
|
|
93
|
+
const startIdx = content.indexOf(MARKER_START);
|
|
94
|
+
const endIdx = content.indexOf(MARKER_END);
|
|
95
|
+
if (startIdx >= 0 && endIdx >= 0) {
|
|
96
|
+
const before = content.slice(0, startIdx);
|
|
97
|
+
const after = content.slice(endIdx + MARKER_END.length);
|
|
98
|
+
content = (before + after).replace(/\n{3,}/g, '\n\n').trim();
|
|
99
|
+
if (content) {
|
|
100
|
+
fs.writeFileSync(heartbeatPath, content + '\n', { mode: 0o640 });
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
fs.unlinkSync(heartbeatPath);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
await withRegistry(workspaceDir, (data) => {
|
|
107
|
+
data.autopilotEnabled = false;
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
text: '**Autopilot disabled.**\nDaily sweeps will no longer run automatically.',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// ── Status ──────────────────────────────────────────────────────────
|
|
114
|
+
async function handleStatus(ctx) {
|
|
115
|
+
const { workspaceDir } = ctx;
|
|
116
|
+
const registry = readRegistry(workspaceDir);
|
|
117
|
+
const enabled = registry.autopilotEnabled;
|
|
118
|
+
const lastRun = registry.lastDoctorAllRunAt ?? 'never';
|
|
119
|
+
const lines = [
|
|
120
|
+
`**Autopilot:** ${enabled ? 'enabled' : 'disabled'}`,
|
|
121
|
+
`**Last doctor-all run:** ${lastRun}`,
|
|
122
|
+
];
|
|
123
|
+
return {
|
|
124
|
+
text: lines.join('\n'),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=autopilot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autopilot.js","sourceRoot":"","sources":["../../src/commands/autopilot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { CommandContext, CommandResult } from './help.js';
|
|
2
|
+
export declare function handleDailyReport(ctx: CommandContext): Promise<CommandResult>;
|
|
3
|
+
export declare function computeHealth(lastMessageAt: string | null, statusContent: string | null, blockers: string): 'fresh' | 'stale' | 'blocked';
|
|
4
|
+
//# sourceMappingURL=daily-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daily-report.d.ts","sourceRoot":"","sources":["../../src/commands/daily-report.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAoFnF;AAoED,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,GAAG,OAAO,GAAG,SAAS,CAW/B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { readRegistry, withRegistry } from '../lib/registry.js';
|
|
4
|
+
import { topicKey } from '../lib/types.js';
|
|
5
|
+
import { buildDailyReport } from '../lib/telegram.js';
|
|
6
|
+
export async function handleDailyReport(ctx) {
|
|
7
|
+
const { workspaceDir, groupId, threadId, logger } = ctx;
|
|
8
|
+
if (!groupId || !threadId) {
|
|
9
|
+
return { text: 'Missing context: must be called from a topic thread.' };
|
|
10
|
+
}
|
|
11
|
+
const key = topicKey(groupId, threadId);
|
|
12
|
+
const registry = readRegistry(workspaceDir);
|
|
13
|
+
const entry = registry.topics[key];
|
|
14
|
+
if (!entry) {
|
|
15
|
+
return { text: 'This topic is not registered. Run /tm init first.' };
|
|
16
|
+
}
|
|
17
|
+
// Dedup: skip if already reported today
|
|
18
|
+
if (entry.lastDoctorReportAt) {
|
|
19
|
+
const lastReport = new Date(entry.lastDoctorReportAt);
|
|
20
|
+
const now = new Date();
|
|
21
|
+
if (lastReport.getUTCFullYear() === now.getUTCFullYear() &&
|
|
22
|
+
lastReport.getUTCMonth() === now.getUTCMonth() &&
|
|
23
|
+
lastReport.getUTCDate() === now.getUTCDate()) {
|
|
24
|
+
return { text: 'Daily report already generated today. Try again tomorrow.' };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const projectsBase = path.join(workspaceDir, 'projects');
|
|
28
|
+
const capsuleDir = path.join(projectsBase, entry.slug);
|
|
29
|
+
if (!fs.existsSync(capsuleDir)) {
|
|
30
|
+
return { text: `Capsule directory not found: projects/${entry.slug}/` };
|
|
31
|
+
}
|
|
32
|
+
// Read capsule files
|
|
33
|
+
const statusContent = readFileOrNull(path.join(capsuleDir, 'STATUS.md'));
|
|
34
|
+
const todoContent = readFileOrNull(path.join(capsuleDir, 'TODO.md'));
|
|
35
|
+
const learningsContent = readFileOrNull(path.join(capsuleDir, 'LEARNINGS.md'));
|
|
36
|
+
// Extract sections
|
|
37
|
+
const doneContent = extractDoneSection(statusContent);
|
|
38
|
+
const newLearnings = extractTodayLearnings(learningsContent);
|
|
39
|
+
const blockers = extractBlockers(todoContent);
|
|
40
|
+
const nextContent = extractNextActions(statusContent);
|
|
41
|
+
const upcomingContent = extractUpcoming(statusContent);
|
|
42
|
+
const health = computeHealth(entry.lastMessageAt, statusContent, blockers);
|
|
43
|
+
const reportText = buildDailyReport({
|
|
44
|
+
name: entry.name,
|
|
45
|
+
doneContent,
|
|
46
|
+
learningsContent: newLearnings,
|
|
47
|
+
blockersContent: blockers,
|
|
48
|
+
nextContent,
|
|
49
|
+
upcomingContent,
|
|
50
|
+
health,
|
|
51
|
+
});
|
|
52
|
+
// Post to topic if postFn available
|
|
53
|
+
if (ctx.postFn) {
|
|
54
|
+
try {
|
|
55
|
+
await ctx.postFn(groupId, threadId, reportText);
|
|
56
|
+
await withRegistry(workspaceDir, (data) => {
|
|
57
|
+
const e = data.topics[key];
|
|
58
|
+
if (e) {
|
|
59
|
+
e.lastDoctorReportAt = new Date().toISOString();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
65
|
+
logger.error(`[daily-report] Post failed: ${msg}`);
|
|
66
|
+
return { text: `Daily report generated but post failed: ${msg}` };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
// Update lastDoctorReportAt even without posting
|
|
71
|
+
await withRegistry(workspaceDir, (data) => {
|
|
72
|
+
const e = data.topics[key];
|
|
73
|
+
if (e) {
|
|
74
|
+
e.lastDoctorReportAt = new Date().toISOString();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return { text: reportText };
|
|
79
|
+
}
|
|
80
|
+
// ── Helpers ────────────────────────────────────────────────────────────
|
|
81
|
+
function readFileOrNull(filePath) {
|
|
82
|
+
try {
|
|
83
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function extractDoneSection(statusContent) {
|
|
90
|
+
if (!statusContent)
|
|
91
|
+
return '_No STATUS.md found._';
|
|
92
|
+
const match = statusContent.match(/^##\s*Last done\s*\(UTC\)\s*\n([\s\S]*?)(?=\n##\s|\n*$)/im);
|
|
93
|
+
if (!match)
|
|
94
|
+
return '_No "Last done" section found._';
|
|
95
|
+
const text = match[1]?.trim();
|
|
96
|
+
return text || '_Empty._';
|
|
97
|
+
}
|
|
98
|
+
function extractTodayLearnings(learningsContent) {
|
|
99
|
+
if (!learningsContent)
|
|
100
|
+
return '_No LEARNINGS.md found._';
|
|
101
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
102
|
+
const lines = learningsContent.split('\n');
|
|
103
|
+
const todayLines = [];
|
|
104
|
+
let inTodaySection = false;
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
if (line.startsWith('## ') && line.includes(today)) {
|
|
107
|
+
inTodaySection = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (inTodaySection && line.startsWith('## ')) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (inTodaySection && line.trim()) {
|
|
114
|
+
todayLines.push(line);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return todayLines.length > 0 ? todayLines.join('\n') : '_None today._';
|
|
118
|
+
}
|
|
119
|
+
function extractBlockers(todoContent) {
|
|
120
|
+
if (!todoContent)
|
|
121
|
+
return '_No TODO.md found._';
|
|
122
|
+
const lines = todoContent.split('\n');
|
|
123
|
+
const blockerLines = lines.filter((l) => /\[BLOCKED\]/i.test(l) || /\bblocked\b/i.test(l));
|
|
124
|
+
return blockerLines.length > 0 ? blockerLines.join('\n') : '_None._';
|
|
125
|
+
}
|
|
126
|
+
function extractNextActions(statusContent) {
|
|
127
|
+
if (!statusContent)
|
|
128
|
+
return '_No STATUS.md found._';
|
|
129
|
+
const match = statusContent.match(/^##\s*Next (?:3 )?actions(?: \(now\))?\s*\n([\s\S]*?)(?=\n##\s|\n*$)/im);
|
|
130
|
+
if (!match)
|
|
131
|
+
return '_No "Next actions" section found._';
|
|
132
|
+
const text = match[1]?.trim();
|
|
133
|
+
return text || '_Empty._';
|
|
134
|
+
}
|
|
135
|
+
function extractUpcoming(statusContent) {
|
|
136
|
+
if (!statusContent)
|
|
137
|
+
return '_No STATUS.md found._';
|
|
138
|
+
const match = statusContent.match(/^##\s*Upcoming actions\s*\n([\s\S]*?)(?=\n##\s|\n*$)/im);
|
|
139
|
+
if (!match)
|
|
140
|
+
return '_No "Upcoming actions" section found._';
|
|
141
|
+
const text = match[1]?.trim();
|
|
142
|
+
return text || '_Empty._';
|
|
143
|
+
}
|
|
144
|
+
export function computeHealth(lastMessageAt, statusContent, blockers) {
|
|
145
|
+
if (blockers && blockers !== '_None._' && blockers !== '_No TODO.md found._') {
|
|
146
|
+
return 'blocked';
|
|
147
|
+
}
|
|
148
|
+
if (!lastMessageAt)
|
|
149
|
+
return 'stale';
|
|
150
|
+
const hoursSinceActivity = (Date.now() - new Date(lastMessageAt).getTime()) / 3_600_000;
|
|
151
|
+
if (hoursSinceActivity > 72)
|
|
152
|
+
return 'stale';
|
|
153
|
+
return 'fresh';
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=daily-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daily-report.js","sourceRoot":"","sources":["../../src/commands/daily-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-all.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-all.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAW/D,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"doctor-all.d.ts","sourceRoot":"","sources":["../../src/commands/doctor-all.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAW/D,wBAAsB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA0OjF"}
|
|
@@ -2,10 +2,9 @@ import * as fs from 'node:fs';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { readRegistry, withRegistry } from '../lib/registry.js';
|
|
4
4
|
import { checkAuthorization } from '../lib/auth.js';
|
|
5
|
-
import { DOCTOR_ALL_COOLDOWN_MS, DOCTOR_PER_TOPIC_CAP_MS, INACTIVE_AFTER_DAYS, SPAM_THRESHOLD, } from '../lib/types.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { runAllChecksForTopic } from '../lib/doctor-checks.js';
|
|
5
|
+
import { DOCTOR_ALL_COOLDOWN_MS, DOCTOR_PER_TOPIC_CAP_MS, INACTIVE_AFTER_DAYS, MAX_POST_ERROR_LENGTH, SPAM_THRESHOLD, } from '../lib/types.js';
|
|
6
|
+
import { buildDoctorReport, buildDoctorButtons, createRateLimitedPoster } from '../lib/telegram.js';
|
|
7
|
+
import { runAllChecksForTopic, backupCapsuleIfHealthy } from '../lib/doctor-checks.js';
|
|
9
8
|
import { includePath } from '../lib/include-generator.js';
|
|
10
9
|
export async function handleDoctorAll(ctx) {
|
|
11
10
|
const { workspaceDir, configDir, userId, logger } = ctx;
|
|
@@ -64,7 +63,9 @@ export async function handleDoctorAll(ctx) {
|
|
|
64
63
|
// Auto-snooze is handled via the check result; we just note it
|
|
65
64
|
logger.info(`[doctor-all] Auto-snoozing ${entry.slug} (${entry.consecutiveSilentDoctors} silent runs)`);
|
|
66
65
|
}
|
|
67
|
-
|
|
66
|
+
// Backup if healthy
|
|
67
|
+
backupCapsuleIfHealthy(projectsBase, entry.slug, results);
|
|
68
|
+
const reportText = buildDoctorReport(entry.name, results, 'html');
|
|
68
69
|
const keyboard = buildDoctorButtons(entry.groupId, entry.threadId, registry.callbackSecret, userId);
|
|
69
70
|
reports.push({
|
|
70
71
|
slug: entry.slug,
|
|
@@ -103,6 +104,40 @@ export async function handleDoctorAll(ctx) {
|
|
|
103
104
|
migrationGroups.push(gid);
|
|
104
105
|
}
|
|
105
106
|
}
|
|
107
|
+
// Fan-out posting: post individual reports to each topic if postFn is available
|
|
108
|
+
let postErrors = 0;
|
|
109
|
+
let postSuccesses = 0;
|
|
110
|
+
if (ctx.postFn && reports.length > 0) {
|
|
111
|
+
const rateLimitedPost = createRateLimitedPoster(ctx.postFn);
|
|
112
|
+
for (const report of reports) {
|
|
113
|
+
try {
|
|
114
|
+
await rateLimitedPost(report.groupId, report.threadId, report.text, report.keyboard);
|
|
115
|
+
postSuccesses++;
|
|
116
|
+
// Update lastDoctorReportAt transactionally on success
|
|
117
|
+
await withRegistry(workspaceDir, (data) => {
|
|
118
|
+
const key = `${report.groupId}:${report.threadId}`;
|
|
119
|
+
const entry = data.topics[key];
|
|
120
|
+
if (entry) {
|
|
121
|
+
entry.lastDoctorReportAt = now.toISOString();
|
|
122
|
+
entry.lastPostError = null;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
postErrors++;
|
|
128
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
129
|
+
logger.error(`[doctor-all] Post failed for ${report.slug}: ${msg}`);
|
|
130
|
+
// Store error on topic entry
|
|
131
|
+
await withRegistry(workspaceDir, (data) => {
|
|
132
|
+
const key = `${report.groupId}:${report.threadId}`;
|
|
133
|
+
const entry = data.topics[key];
|
|
134
|
+
if (entry) {
|
|
135
|
+
entry.lastPostError = msg.slice(0, MAX_POST_ERROR_LENGTH);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
106
141
|
// Update registry: lastDoctorAllRunAt and per-topic timestamps
|
|
107
142
|
await withRegistry(workspaceDir, (data) => {
|
|
108
143
|
data.lastDoctorAllRunAt = now.toISOString();
|
|
@@ -137,17 +172,20 @@ export async function handleDoctorAll(ctx) {
|
|
|
137
172
|
});
|
|
138
173
|
// Build summary
|
|
139
174
|
const lines = [
|
|
140
|
-
|
|
175
|
+
`**Doctor All Summary**`,
|
|
141
176
|
'',
|
|
142
177
|
`Processed: ${processed}`,
|
|
143
178
|
`Skipped (ineligible): ${skipped}`,
|
|
144
179
|
`Total: ${allEntries.length}`,
|
|
145
180
|
];
|
|
181
|
+
if (ctx.postFn) {
|
|
182
|
+
lines.push(`Posted: ${postSuccesses}, Post failures: ${postErrors}`);
|
|
183
|
+
}
|
|
146
184
|
if (errors.length > 0) {
|
|
147
185
|
lines.push('');
|
|
148
|
-
lines.push(
|
|
186
|
+
lines.push(`**Errors (${errors.length}):**`);
|
|
149
187
|
for (const e of errors.slice(0, 10)) {
|
|
150
|
-
lines.push(`- ${
|
|
188
|
+
lines.push(`- ${e}`);
|
|
151
189
|
}
|
|
152
190
|
if (errors.length > 10) {
|
|
153
191
|
lines.push(`... and ${errors.length - 10} more`);
|
|
@@ -155,17 +193,13 @@ export async function handleDoctorAll(ctx) {
|
|
|
155
193
|
}
|
|
156
194
|
if (migrationGroups.length > 0) {
|
|
157
195
|
lines.push('');
|
|
158
|
-
lines.push('
|
|
196
|
+
lines.push('**Possible group migrations detected:**');
|
|
159
197
|
for (const gid of migrationGroups) {
|
|
160
|
-
lines.push(`- Group ${
|
|
198
|
+
lines.push(`- Group ${gid}: all topics failed. Check for group migration.`);
|
|
161
199
|
}
|
|
162
200
|
}
|
|
163
|
-
// Per-topic reports are returned as a summary for the calling context.
|
|
164
|
-
// The tool router is responsible for posting individual reports to each topic.
|
|
165
|
-
// Here we return the summary to the current thread.
|
|
166
201
|
return {
|
|
167
202
|
text: lines.join('\n'),
|
|
168
|
-
parseMode: 'HTML',
|
|
169
203
|
};
|
|
170
204
|
}
|
|
171
205
|
function isEligible(entry, now) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor-all.js","sourceRoot":"","sources":["../../src/commands/doctor-all.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"doctor-all.js","sourceRoot":"","sources":["../../src/commands/doctor-all.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/D,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA0F9E"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -5,7 +5,7 @@ import { checkAuthorization } from '../lib/auth.js';
|
|
|
5
5
|
import { topicKey } from '../lib/types.js';
|
|
6
6
|
import { jailCheck, rejectSymlink } from '../lib/security.js';
|
|
7
7
|
import { buildDoctorReport, buildDoctorButtons } from '../lib/telegram.js';
|
|
8
|
-
import { runAllChecksForTopic } from '../lib/doctor-checks.js';
|
|
8
|
+
import { runAllChecksForTopic, backupCapsuleIfHealthy } from '../lib/doctor-checks.js';
|
|
9
9
|
import { includePath } from '../lib/include-generator.js';
|
|
10
10
|
export async function handleDoctor(ctx) {
|
|
11
11
|
const { workspaceDir, configDir, userId, groupId, threadId } = ctx;
|
|
@@ -46,6 +46,8 @@ export async function handleDoctor(ctx) {
|
|
|
46
46
|
// Run all checks
|
|
47
47
|
const cronJobsPath = path.join(configDir, 'cron', 'jobs.json');
|
|
48
48
|
const results = runAllChecksForTopic(entry, projectsBase, includeContent, registry, cronJobsPath);
|
|
49
|
+
// Backup if healthy
|
|
50
|
+
backupCapsuleIfHealthy(projectsBase, entry.slug, results);
|
|
49
51
|
// Build report
|
|
50
52
|
const reportText = buildDoctorReport(entry.name, results);
|
|
51
53
|
// Build inline keyboard with HMAC-signed callbacks
|
|
@@ -67,7 +69,6 @@ export async function handleDoctor(ctx) {
|
|
|
67
69
|
});
|
|
68
70
|
return {
|
|
69
71
|
text: reportText + textCommands,
|
|
70
|
-
parseMode: 'HTML',
|
|
71
72
|
inlineKeyboard: keyboard,
|
|
72
73
|
};
|
|
73
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|