@songsid/agend 0.0.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/README.md +210 -0
- package/README.zh-TW.md +134 -0
- package/dist/access-path.d.ts +10 -0
- package/dist/access-path.js +32 -0
- package/dist/access-path.js.map +1 -0
- package/dist/adapter-world.d.ts +25 -0
- package/dist/adapter-world.js +41 -0
- package/dist/adapter-world.js.map +1 -0
- package/dist/agent-cli-instructions.md +50 -0
- package/dist/agent-cli.d.ts +2 -0
- package/dist/agent-cli.js +200 -0
- package/dist/agent-cli.js.map +1 -0
- package/dist/agent-endpoint.d.ts +25 -0
- package/dist/agent-endpoint.js +162 -0
- package/dist/agent-endpoint.js.map +1 -0
- package/dist/backend/antigravity.d.ts +17 -0
- package/dist/backend/antigravity.js +98 -0
- package/dist/backend/antigravity.js.map +1 -0
- package/dist/backend/claude-code.d.ts +23 -0
- package/dist/backend/claude-code.js +171 -0
- package/dist/backend/claude-code.js.map +1 -0
- package/dist/backend/codex.d.ts +18 -0
- package/dist/backend/codex.js +160 -0
- package/dist/backend/codex.js.map +1 -0
- package/dist/backend/factory.d.ts +2 -0
- package/dist/backend/factory.js +28 -0
- package/dist/backend/factory.js.map +1 -0
- package/dist/backend/gemini-cli.d.ts +17 -0
- package/dist/backend/gemini-cli.js +163 -0
- package/dist/backend/gemini-cli.js.map +1 -0
- package/dist/backend/index.d.ts +7 -0
- package/dist/backend/index.js +7 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/kiro.d.ts +17 -0
- package/dist/backend/kiro.js +147 -0
- package/dist/backend/kiro.js.map +1 -0
- package/dist/backend/marker-utils.d.ts +13 -0
- package/dist/backend/marker-utils.js +64 -0
- package/dist/backend/marker-utils.js.map +1 -0
- package/dist/backend/mock.d.ts +25 -0
- package/dist/backend/mock.js +85 -0
- package/dist/backend/mock.js.map +1 -0
- package/dist/backend/opencode.d.ts +16 -0
- package/dist/backend/opencode.js +136 -0
- package/dist/backend/opencode.js.map +1 -0
- package/dist/backend/types.d.ts +86 -0
- package/dist/backend/types.js +33 -0
- package/dist/backend/types.js.map +1 -0
- package/dist/channel/access-manager.d.ts +18 -0
- package/dist/channel/access-manager.js +153 -0
- package/dist/channel/access-manager.js.map +1 -0
- package/dist/channel/adapters/telegram.d.ts +63 -0
- package/dist/channel/adapters/telegram.js +646 -0
- package/dist/channel/adapters/telegram.js.map +1 -0
- package/dist/channel/attachment-handler.d.ts +15 -0
- package/dist/channel/attachment-handler.js +88 -0
- package/dist/channel/attachment-handler.js.map +1 -0
- package/dist/channel/factory.d.ts +12 -0
- package/dist/channel/factory.js +67 -0
- package/dist/channel/factory.js.map +1 -0
- package/dist/channel/ipc-bridge.d.ts +26 -0
- package/dist/channel/ipc-bridge.js +220 -0
- package/dist/channel/ipc-bridge.js.map +1 -0
- package/dist/channel/mcp-server.d.ts +10 -0
- package/dist/channel/mcp-server.js +288 -0
- package/dist/channel/mcp-server.js.map +1 -0
- package/dist/channel/mcp-tools.d.ts +17 -0
- package/dist/channel/mcp-tools.js +110 -0
- package/dist/channel/mcp-tools.js.map +1 -0
- package/dist/channel/message-bus.d.ts +17 -0
- package/dist/channel/message-bus.js +86 -0
- package/dist/channel/message-bus.js.map +1 -0
- package/dist/channel/message-queue.d.ts +39 -0
- package/dist/channel/message-queue.js +253 -0
- package/dist/channel/message-queue.js.map +1 -0
- package/dist/channel/tool-router.d.ts +6 -0
- package/dist/channel/tool-router.js +75 -0
- package/dist/channel/tool-router.js.map +1 -0
- package/dist/channel/tool-tracker.d.ts +13 -0
- package/dist/channel/tool-tracker.js +58 -0
- package/dist/channel/tool-tracker.js.map +1 -0
- package/dist/channel/types.d.ts +118 -0
- package/dist/channel/types.js +2 -0
- package/dist/channel/types.js.map +1 -0
- package/dist/chat-export.d.ts +4 -0
- package/dist/chat-export.js +91 -0
- package/dist/chat-export.js.map +1 -0
- package/dist/classic-channel-manager.d.ts +59 -0
- package/dist/classic-channel-manager.js +193 -0
- package/dist/classic-channel-manager.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1833 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +118 -0
- package/dist/config.js.map +1 -0
- package/dist/context-guardian.d.ts +26 -0
- package/dist/context-guardian.js +73 -0
- package/dist/context-guardian.js.map +1 -0
- package/dist/cost-guard.d.ts +36 -0
- package/dist/cost-guard.js +147 -0
- package/dist/cost-guard.js.map +1 -0
- package/dist/daemon-entry.d.ts +1 -0
- package/dist/daemon-entry.js +29 -0
- package/dist/daemon-entry.js.map +1 -0
- package/dist/daemon.d.ts +152 -0
- package/dist/daemon.js +1714 -0
- package/dist/daemon.js.map +1 -0
- package/dist/daily-summary.d.ts +13 -0
- package/dist/daily-summary.js +55 -0
- package/dist/daily-summary.js.map +1 -0
- package/dist/event-log.d.ts +36 -0
- package/dist/event-log.js +100 -0
- package/dist/event-log.js.map +1 -0
- package/dist/export-import.d.ts +2 -0
- package/dist/export-import.js +162 -0
- package/dist/export-import.js.map +1 -0
- package/dist/fleet-context.d.ts +61 -0
- package/dist/fleet-context.js +4 -0
- package/dist/fleet-context.js.map +1 -0
- package/dist/fleet-dashboard-html.d.ts +6 -0
- package/dist/fleet-dashboard-html.js +443 -0
- package/dist/fleet-dashboard-html.js.map +1 -0
- package/dist/fleet-health-server.d.ts +35 -0
- package/dist/fleet-health-server.js +290 -0
- package/dist/fleet-health-server.js.map +1 -0
- package/dist/fleet-instructions.d.ts +5 -0
- package/dist/fleet-instructions.js +161 -0
- package/dist/fleet-instructions.js.map +1 -0
- package/dist/fleet-manager.d.ts +212 -0
- package/dist/fleet-manager.js +3655 -0
- package/dist/fleet-manager.js.map +1 -0
- package/dist/fleet-rpc-handlers.d.ts +42 -0
- package/dist/fleet-rpc-handlers.js +356 -0
- package/dist/fleet-rpc-handlers.js.map +1 -0
- package/dist/fleet-system-prompt.d.ts +11 -0
- package/dist/fleet-system-prompt.js +61 -0
- package/dist/fleet-system-prompt.js.map +1 -0
- package/dist/general-knowledge/skills.md +177 -0
- package/dist/hang-detector.d.ts +16 -0
- package/dist/hang-detector.js +53 -0
- package/dist/hang-detector.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/instance-lifecycle.d.ts +90 -0
- package/dist/instance-lifecycle.js +592 -0
- package/dist/instance-lifecycle.js.map +1 -0
- package/dist/instructions.d.ts +15 -0
- package/dist/instructions.js +90 -0
- package/dist/instructions.js.map +1 -0
- package/dist/logger.d.ts +7 -0
- package/dist/logger.js +84 -0
- package/dist/logger.js.map +1 -0
- package/dist/outbound-handlers.d.ts +51 -0
- package/dist/outbound-handlers.js +739 -0
- package/dist/outbound-handlers.js.map +1 -0
- package/dist/outbound-schemas.d.ts +238 -0
- package/dist/outbound-schemas.js +248 -0
- package/dist/outbound-schemas.js.map +1 -0
- package/dist/paths.d.ts +10 -0
- package/dist/paths.js +42 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugin/agend/.claude-plugin/plugin.json +5 -0
- package/dist/quickstart.d.ts +1 -0
- package/dist/quickstart.js +595 -0
- package/dist/quickstart.js.map +1 -0
- package/dist/routing-engine.d.ts +22 -0
- package/dist/routing-engine.js +44 -0
- package/dist/routing-engine.js.map +1 -0
- package/dist/safe-async.d.ts +6 -0
- package/dist/safe-async.js +20 -0
- package/dist/safe-async.js.map +1 -0
- package/dist/scheduler/db.d.ts +37 -0
- package/dist/scheduler/db.js +360 -0
- package/dist/scheduler/db.js.map +1 -0
- package/dist/scheduler/db.test.d.ts +1 -0
- package/dist/scheduler/db.test.js +92 -0
- package/dist/scheduler/db.test.js.map +1 -0
- package/dist/scheduler/index.d.ts +4 -0
- package/dist/scheduler/index.js +4 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +44 -0
- package/dist/scheduler/scheduler.js +197 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/scheduler.test.d.ts +1 -0
- package/dist/scheduler/scheduler.test.js +119 -0
- package/dist/scheduler/scheduler.test.js.map +1 -0
- package/dist/scheduler/types.d.ts +107 -0
- package/dist/scheduler/types.js +7 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/service-installer.d.ts +17 -0
- package/dist/service-installer.js +182 -0
- package/dist/service-installer.js.map +1 -0
- package/dist/setup-wizard.d.ts +48 -0
- package/dist/setup-wizard.js +701 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/statusline-watcher.d.ts +34 -0
- package/dist/statusline-watcher.js +73 -0
- package/dist/statusline-watcher.js.map +1 -0
- package/dist/stt.d.ts +10 -0
- package/dist/stt.js +33 -0
- package/dist/stt.js.map +1 -0
- package/dist/tmux-control.d.ts +52 -0
- package/dist/tmux-control.js +207 -0
- package/dist/tmux-control.js.map +1 -0
- package/dist/tmux-manager.d.ts +44 -0
- package/dist/tmux-manager.js +218 -0
- package/dist/tmux-manager.js.map +1 -0
- package/dist/topic-archiver.d.ts +40 -0
- package/dist/topic-archiver.js +103 -0
- package/dist/topic-archiver.js.map +1 -0
- package/dist/topic-commands.d.ts +28 -0
- package/dist/topic-commands.js +359 -0
- package/dist/topic-commands.js.map +1 -0
- package/dist/transcript-monitor.d.ts +23 -0
- package/dist/transcript-monitor.js +164 -0
- package/dist/transcript-monitor.js.map +1 -0
- package/dist/types.d.ts +211 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/dashboard.html +719 -0
- package/dist/web-api.d.ts +101 -0
- package/dist/web-api.js +648 -0
- package/dist/web-api.js.map +1 -0
- package/dist/webhook-emitter.d.ts +15 -0
- package/dist/webhook-emitter.js +41 -0
- package/dist/webhook-emitter.js.map +1 -0
- package/dist/workflow-templates/default.md +35 -0
- package/package.json +76 -0
- package/templates/launchd.plist.ejs +31 -0
- package/templates/systemd.service.ejs +16 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const exec = promisify(execFile);
|
|
4
|
+
export class TmuxManager {
|
|
5
|
+
sessionName;
|
|
6
|
+
windowId;
|
|
7
|
+
// Socket isolation: null = use tmux default socket (backward compatible).
|
|
8
|
+
// Set to a name to use `-L <name>` for custom AGEND_HOME isolation.
|
|
9
|
+
static socketName = null;
|
|
10
|
+
static setSocketName(name) {
|
|
11
|
+
TmuxManager.socketName = name;
|
|
12
|
+
}
|
|
13
|
+
/** Prefix tmux args with -L when socket isolation is active. */
|
|
14
|
+
static tmuxArgs(args) {
|
|
15
|
+
if (!TmuxManager.socketName)
|
|
16
|
+
return args;
|
|
17
|
+
return ["-L", TmuxManager.socketName, ...args];
|
|
18
|
+
}
|
|
19
|
+
constructor(sessionName, windowId) {
|
|
20
|
+
this.sessionName = sessionName;
|
|
21
|
+
this.windowId = windowId;
|
|
22
|
+
}
|
|
23
|
+
// === Static session-level methods ===
|
|
24
|
+
static async ensureSession(name) {
|
|
25
|
+
if (await TmuxManager.sessionExists(name))
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
await exec("tmux", TmuxManager.tmuxArgs(["new-session", "-d", "-s", name]));
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
if (String(err).includes("duplicate session"))
|
|
32
|
+
return;
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
static async sessionExists(name) {
|
|
37
|
+
try {
|
|
38
|
+
await exec("tmux", TmuxManager.tmuxArgs(["has-session", "-t", name]));
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
static async killSession(name) {
|
|
46
|
+
try {
|
|
47
|
+
await exec("tmux", TmuxManager.tmuxArgs(["kill-session", "-t", name]));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Expected if session doesn't exist
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
static async listWindows(sessionName) {
|
|
54
|
+
try {
|
|
55
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
56
|
+
"list-windows", "-t", sessionName, "-F", "#{window_id}|||#{window_name}"
|
|
57
|
+
]));
|
|
58
|
+
return stdout.trim().split("\n").filter(Boolean).map(line => {
|
|
59
|
+
const [id, name] = line.split("|||");
|
|
60
|
+
return { id, name };
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
static async getPanePid(sessionName, windowId) {
|
|
68
|
+
try {
|
|
69
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
70
|
+
"list-panes", "-t", `${sessionName}:${windowId}`, "-F", "#{pane_pid}",
|
|
71
|
+
]));
|
|
72
|
+
const pid = parseInt(stdout.trim(), 10);
|
|
73
|
+
return isNaN(pid) ? null : pid;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// === Instance window methods ===
|
|
80
|
+
async createWindow(command, cwd, windowName) {
|
|
81
|
+
const args = ["new-window", "-a", "-t", this.sessionName, "-c", cwd];
|
|
82
|
+
if (windowName)
|
|
83
|
+
args.push("-n", windowName);
|
|
84
|
+
args.push("-P", "-F", "#{window_id}", command);
|
|
85
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs(args));
|
|
86
|
+
this.windowId = stdout.trim();
|
|
87
|
+
if (windowName) {
|
|
88
|
+
await exec("tmux", TmuxManager.tmuxArgs(["set-window-option", "-t", `${this.sessionName}:${this.windowId}`, "allow-rename", "off"])).catch(() => { });
|
|
89
|
+
}
|
|
90
|
+
return this.windowId;
|
|
91
|
+
}
|
|
92
|
+
async killWindow() {
|
|
93
|
+
if (!this.windowId)
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
await exec("tmux", TmuxManager.tmuxArgs(["kill-window", "-t", `${this.sessionName}:${this.windowId}`]));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Expected if window already exited
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if the tmux window still exists in the session.
|
|
104
|
+
* Note: with remain-on-exit enabled, a window with a dead pane still
|
|
105
|
+
* returns true. Use getPaneStatus() to distinguish alive vs dead pane.
|
|
106
|
+
*/
|
|
107
|
+
async isWindowAlive() {
|
|
108
|
+
if (!this.windowId)
|
|
109
|
+
return false;
|
|
110
|
+
try {
|
|
111
|
+
const windows = await TmuxManager.listWindows(this.sessionName);
|
|
112
|
+
return windows.some(w => w.id === this.windowId);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/** Enable remain-on-exit so dead panes are preserved for exit code capture. */
|
|
119
|
+
async setRemainOnExit() {
|
|
120
|
+
await exec("tmux", TmuxManager.tmuxArgs([
|
|
121
|
+
"set-option", "-t", `${this.sessionName}:${this.windowId}`,
|
|
122
|
+
"remain-on-exit", "on",
|
|
123
|
+
]));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get pane status. Returns null if the window doesn't exist.
|
|
127
|
+
* When remain-on-exit is enabled, a dead pane has alive=false with exitCode.
|
|
128
|
+
* exitCode is undefined if tmux version doesn't support pane_dead_status (< 3.1).
|
|
129
|
+
*/
|
|
130
|
+
async getPaneStatus() {
|
|
131
|
+
if (!this.windowId)
|
|
132
|
+
return null;
|
|
133
|
+
try {
|
|
134
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
135
|
+
"list-panes", "-t", `${this.sessionName}:${this.windowId}`,
|
|
136
|
+
"-F", "#{pane_dead} #{pane_dead_status}",
|
|
137
|
+
]));
|
|
138
|
+
const line = stdout.trim().split("\n")[0];
|
|
139
|
+
if (!line)
|
|
140
|
+
return null;
|
|
141
|
+
const parts = line.split(" ");
|
|
142
|
+
const dead = parts[0];
|
|
143
|
+
if (dead === "1") {
|
|
144
|
+
const code = parseInt(parts[1], 10);
|
|
145
|
+
return { alive: false, exitCode: Number.isNaN(code) ? undefined : code };
|
|
146
|
+
}
|
|
147
|
+
return { alive: true };
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async sendKeys(text) {
|
|
154
|
+
try {
|
|
155
|
+
await exec("tmux", TmuxManager.tmuxArgs(["send-keys", "-l", "-t", `${this.sessionName}:${this.windowId}`, text]));
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async sendSpecialKey(key) {
|
|
163
|
+
try {
|
|
164
|
+
await exec("tmux", TmuxManager.tmuxArgs(["send-keys", "-t", `${this.sessionName}:${this.windowId}`, key]));
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async pasteText(text) {
|
|
172
|
+
try {
|
|
173
|
+
const target = `${this.sessionName}:${this.windowId}`;
|
|
174
|
+
const bufName = `paste-${this.windowId}-${Date.now()}`;
|
|
175
|
+
await exec("tmux", TmuxManager.tmuxArgs(["set-buffer", "-b", bufName, "--", text]));
|
|
176
|
+
await exec("tmux", TmuxManager.tmuxArgs(["paste-buffer", "-d", "-b", bufName, "-t", target, "-p"]));
|
|
177
|
+
await new Promise(r => setTimeout(r, 500));
|
|
178
|
+
await exec("tmux", TmuxManager.tmuxArgs(["send-keys", "-t", target, "Enter"]));
|
|
179
|
+
// Retry Enter: if TUI was busy outputting, the first Enter may be swallowed.
|
|
180
|
+
// A second Enter on an empty input is a no-op for all supported CLIs.
|
|
181
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
182
|
+
await exec("tmux", TmuxManager.tmuxArgs(["send-keys", "-t", target, "Enter"]));
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async pipeOutput(logPath) {
|
|
190
|
+
// pipe-pane's shell command runs via /bin/sh -c. Reject control characters
|
|
191
|
+
// that would break out of the single-quote escape (newlines, NULs, etc.);
|
|
192
|
+
// only expect absolute paths produced by getAgendHome() / instanceDir.
|
|
193
|
+
if (/[\x00-\x1f]/.test(logPath)) {
|
|
194
|
+
throw new Error(`Invalid log path (contains control characters): ${JSON.stringify(logPath)}`);
|
|
195
|
+
}
|
|
196
|
+
const escaped = logPath.replace(/'/g, "'\\''");
|
|
197
|
+
await exec("tmux", TmuxManager.tmuxArgs([
|
|
198
|
+
"pipe-pane", "-t", `${this.sessionName}:${this.windowId}`,
|
|
199
|
+
`cat >> '${escaped}'`,
|
|
200
|
+
]));
|
|
201
|
+
}
|
|
202
|
+
async capturePane() {
|
|
203
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
204
|
+
"capture-pane", "-t", `${this.sessionName}:${this.windowId}`, "-p",
|
|
205
|
+
]));
|
|
206
|
+
return stdout;
|
|
207
|
+
}
|
|
208
|
+
/** Capture pane content including scrollback history (last N lines). */
|
|
209
|
+
async capturePaneWithHistory(lines = 50) {
|
|
210
|
+
const { stdout } = await exec("tmux", TmuxManager.tmuxArgs([
|
|
211
|
+
"capture-pane", "-t", `${this.sessionName}:${this.windowId}`,
|
|
212
|
+
"-p", "-S", `-${lines}`,
|
|
213
|
+
]));
|
|
214
|
+
return stdout;
|
|
215
|
+
}
|
|
216
|
+
getWindowId() { return this.windowId; }
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=tmux-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux-manager.js","sourceRoot":"","sources":["../src/tmux-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FleetConfig } from "./types.js";
|
|
2
|
+
import type { ChannelAdapter } from "./channel/types.js";
|
|
3
|
+
import type { Logger } from "./logger.js";
|
|
4
|
+
export interface ArchiverContext {
|
|
5
|
+
readonly fleetConfig: FleetConfig | null;
|
|
6
|
+
readonly adapter: ChannelAdapter | null;
|
|
7
|
+
readonly logger: Logger;
|
|
8
|
+
readonly dataDir: string;
|
|
9
|
+
getInstanceStatus(name: string): "running" | "stopped" | "crashed";
|
|
10
|
+
lastActivityMs(name: string): number;
|
|
11
|
+
setTopicIcon(name: string, state: "green" | "blue" | "red" | "remove"): void;
|
|
12
|
+
touchActivity(name: string): void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Manages automatic archival (close) and reopening of idle forum topics.
|
|
16
|
+
*
|
|
17
|
+
* Archived state is persisted to `<dataDir>/archived-topics.json` so a daemon
|
|
18
|
+
* restart does not re-archive (or re-message) topics that were already closed.
|
|
19
|
+
*/
|
|
20
|
+
export declare class TopicArchiver {
|
|
21
|
+
private ctx;
|
|
22
|
+
private archived;
|
|
23
|
+
private timer;
|
|
24
|
+
private readonly statePath;
|
|
25
|
+
static readonly IDLE_MS: number;
|
|
26
|
+
private static readonly POLL_MS;
|
|
27
|
+
constructor(ctx: ArchiverContext);
|
|
28
|
+
private load;
|
|
29
|
+
private save;
|
|
30
|
+
/** Is this topic currently archived? */
|
|
31
|
+
isArchived(topicId: string): boolean;
|
|
32
|
+
/** Start periodic idle check. */
|
|
33
|
+
startPoller(): void;
|
|
34
|
+
/** Stop the poller and clean up. */
|
|
35
|
+
stop(): void;
|
|
36
|
+
/** Close topics that have been idle beyond threshold. */
|
|
37
|
+
archiveIdle(): Promise<void>;
|
|
38
|
+
/** Reopen an archived topic and restore icon. */
|
|
39
|
+
reopen(topicId: string, instanceName: string): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Manages automatic archival (close) and reopening of idle forum topics.
|
|
5
|
+
*
|
|
6
|
+
* Archived state is persisted to `<dataDir>/archived-topics.json` so a daemon
|
|
7
|
+
* restart does not re-archive (or re-message) topics that were already closed.
|
|
8
|
+
*/
|
|
9
|
+
export class TopicArchiver {
|
|
10
|
+
ctx;
|
|
11
|
+
archived = new Set();
|
|
12
|
+
timer = null;
|
|
13
|
+
statePath;
|
|
14
|
+
static IDLE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
15
|
+
static POLL_MS = 30 * 60_000; // check every 30 minutes
|
|
16
|
+
constructor(ctx) {
|
|
17
|
+
this.ctx = ctx;
|
|
18
|
+
this.statePath = join(ctx.dataDir, "archived-topics.json");
|
|
19
|
+
this.load();
|
|
20
|
+
}
|
|
21
|
+
load() {
|
|
22
|
+
if (!existsSync(this.statePath))
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
const arr = JSON.parse(readFileSync(this.statePath, "utf-8"));
|
|
26
|
+
if (!Array.isArray(arr))
|
|
27
|
+
return;
|
|
28
|
+
for (const id of arr) {
|
|
29
|
+
if (typeof id === "string")
|
|
30
|
+
this.archived.add(id);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
this.ctx.logger.warn({ err, path: this.statePath }, "Failed to load archived-topics state");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
save() {
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(this.statePath, JSON.stringify([...this.archived]));
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
this.ctx.logger.warn({ err, path: this.statePath }, "Failed to save archived-topics state");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Is this topic currently archived? */
|
|
46
|
+
isArchived(topicId) {
|
|
47
|
+
return this.archived.has(topicId);
|
|
48
|
+
}
|
|
49
|
+
/** Start periodic idle check. */
|
|
50
|
+
startPoller() {
|
|
51
|
+
this.timer = setInterval(() => {
|
|
52
|
+
this.archiveIdle().catch((err) => this.ctx.logger.debug({ err }, "Archive idle check failed"));
|
|
53
|
+
}, TopicArchiver.POLL_MS);
|
|
54
|
+
}
|
|
55
|
+
/** Stop the poller and clean up. */
|
|
56
|
+
stop() {
|
|
57
|
+
if (this.timer) {
|
|
58
|
+
clearInterval(this.timer);
|
|
59
|
+
this.timer = null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Close topics that have been idle beyond threshold. */
|
|
63
|
+
async archiveIdle() {
|
|
64
|
+
if (!this.ctx.adapter?.closeForumTopic || !this.ctx.fleetConfig)
|
|
65
|
+
return;
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
for (const [name, config] of Object.entries(this.ctx.fleetConfig.instances)) {
|
|
68
|
+
const topicId = config.topic_id;
|
|
69
|
+
if (topicId == null || config.general_topic)
|
|
70
|
+
continue;
|
|
71
|
+
const topicIdStr = String(topicId);
|
|
72
|
+
if (this.archived.has(topicIdStr))
|
|
73
|
+
continue;
|
|
74
|
+
const status = this.ctx.getInstanceStatus(name);
|
|
75
|
+
if (status !== "running")
|
|
76
|
+
continue;
|
|
77
|
+
const last = this.ctx.lastActivityMs(name);
|
|
78
|
+
if (last === 0)
|
|
79
|
+
continue; // never active → skip
|
|
80
|
+
if (now - last < TopicArchiver.IDLE_MS)
|
|
81
|
+
continue;
|
|
82
|
+
this.ctx.logger.info({ name, topicId, idleHours: Math.round((now - last) / 3600000) }, "Archiving idle topic");
|
|
83
|
+
this.archived.add(topicIdStr);
|
|
84
|
+
this.save();
|
|
85
|
+
this.ctx.setTopicIcon(name, "remove");
|
|
86
|
+
await this.ctx.adapter.closeForumTopic(topicId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** Reopen an archived topic and restore icon. */
|
|
90
|
+
async reopen(topicId, instanceName) {
|
|
91
|
+
if (!this.archived.has(topicId))
|
|
92
|
+
return;
|
|
93
|
+
this.archived.delete(topicId);
|
|
94
|
+
this.save();
|
|
95
|
+
if (this.ctx.adapter?.reopenForumTopic) {
|
|
96
|
+
await this.ctx.adapter.reopenForumTopic(topicId);
|
|
97
|
+
}
|
|
98
|
+
this.ctx.setTopicIcon(instanceName, "green");
|
|
99
|
+
this.ctx.touchActivity(instanceName);
|
|
100
|
+
this.ctx.logger.info({ instanceName, topicId }, "Reopened archived topic");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=topic-archiver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topic-archiver.js","sourceRoot":"","sources":["../src/topic-archiver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FleetContext } from "./fleet-context.js";
|
|
2
|
+
import type { InboundMessage } from "./channel/types.js";
|
|
3
|
+
/** Sanitize a directory name into a valid instance name. Keeps Unicode letters (incl. CJK). */
|
|
4
|
+
export declare function sanitizeInstanceName(name: string): string;
|
|
5
|
+
export declare class TopicCommands {
|
|
6
|
+
private ctx;
|
|
7
|
+
constructor(ctx: FleetContext);
|
|
8
|
+
/** Get the adapter that should reply to a given inbound message */
|
|
9
|
+
private getReplyAdapter;
|
|
10
|
+
/** Parse and dispatch commands from the General topic */
|
|
11
|
+
handleGeneralCommand(msg: InboundMessage): Promise<boolean>;
|
|
12
|
+
/** Handle /ctx in any instance topic — returns true if handled */
|
|
13
|
+
handleInstanceCommand(msg: InboundMessage, instanceName: string): Promise<boolean>;
|
|
14
|
+
private handleRestartCommand;
|
|
15
|
+
private handleStatusCommand;
|
|
16
|
+
private handleSysInfoCommand;
|
|
17
|
+
private handleUpdateCommand;
|
|
18
|
+
/** Reply with redirect when message arrives in an unbound topic */
|
|
19
|
+
handleUnboundTopic(msg: InboundMessage): Promise<void>;
|
|
20
|
+
/** Handle topic deletion — stop daemon and remove from config */
|
|
21
|
+
handleTopicDeleted(threadId: string): Promise<void>;
|
|
22
|
+
/** Create instance config, save fleet.yaml, start daemon, connect IPC. */
|
|
23
|
+
bindAndStart(dirPath: string, topicId: number | string): Promise<string>;
|
|
24
|
+
/** Create Telegram topics for instances that don't have topic_id */
|
|
25
|
+
autoCreateTopics(): Promise<void>;
|
|
26
|
+
/** Register bot commands in Telegram command menu */
|
|
27
|
+
registerBotCommands(): Promise<void>;
|
|
28
|
+
}
|