maestro-agent 0.0.1 → 0.0.3
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 +316 -2
- package/bin/maestro.ts +5 -0
- package/dist/maestro +0 -0
- package/dist/web/apple-touch-icon.png +0 -0
- package/dist/web/assets/Connections-BMA04Ycg.js +11 -0
- package/dist/web/assets/GanttView-DXjh0gxg.js +49 -0
- package/dist/web/assets/Home-Ct3Ho0Qt.js +1 -0
- package/dist/web/assets/HooksCrons--0kyVJcR.js +11 -0
- package/dist/web/assets/ProjectDetail-B_IqEpFu.js +1 -0
- package/dist/web/assets/Roles-D1tIQzto.js +24 -0
- package/dist/web/assets/Settings-yts4LUmH.js +11 -0
- package/dist/web/assets/Skills-DbuNLjIV.js +12 -0
- package/dist/web/assets/Wizard-vJol8-Y4.js +11 -0
- package/dist/web/assets/WorkspaceChat-DrsLs4m2.js +56 -0
- package/dist/web/assets/WorkspaceDashboard-B9vgrd2Z.js +6 -0
- package/dist/web/assets/WorkspaceNew-DoNGYHCG.js +1 -0
- package/dist/web/assets/WorkspaceProjects-DDp3mUse.js +6 -0
- package/dist/web/assets/WorkspaceSchedules-BTjmCbYG.js +1 -0
- package/dist/web/assets/WorkspaceTasks-mPU-bhKR.js +41 -0
- package/dist/web/assets/activity-CIA8bIA4.js +6 -0
- package/dist/web/assets/addon-fit-BlxrFPDK.js +1 -0
- package/dist/web/assets/arrow-right-S7ID7nDp.js +6 -0
- package/dist/web/assets/badge-DDTUzWIi.js +1 -0
- package/dist/web/assets/circle-check-B3P1qK0Z.js +6 -0
- package/dist/web/assets/clock-f9aYZox0.js +6 -0
- package/dist/web/assets/index-BRo4Du_s.js +11 -0
- package/dist/web/assets/index-C7kx39S9.js +196 -0
- package/dist/web/assets/index-D6LSdZea.css +1 -0
- package/dist/web/assets/plus-BHnOxbns.js +6 -0
- package/dist/web/assets/refresh-cw-BWX04Hg3.js +6 -0
- package/dist/web/assets/save-BLbb_9xz.js +6 -0
- package/dist/web/assets/sparkles-CDr6Dw1e.js +6 -0
- package/dist/web/assets/trash-2-9-ThEdey.js +6 -0
- package/dist/web/assets/useEventStream-DXt2Hmei.js +1 -0
- package/dist/web/assets/x-DVdKPXXy.js +6 -0
- package/dist/web/assets/xterm-DYP7pi_n.css +32 -0
- package/dist/web/assets/xterm-DlVFs1Kw.js +9 -0
- package/dist/web/favicon-512.png +0 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/index.html +15 -0
- package/package.json +49 -6
- package/src/api/agents.ts +76 -0
- package/src/api/audit.ts +19 -0
- package/src/api/autopilot.ts +73 -0
- package/src/api/chat.ts +801 -0
- package/src/api/chief.ts +84 -0
- package/src/api/config.ts +39 -0
- package/src/api/gantt.ts +72 -0
- package/src/api/hooks.ts +54 -0
- package/src/api/inbox.ts +125 -0
- package/src/api/lark.ts +32 -0
- package/src/api/memory.ts +37 -0
- package/src/api/ops.ts +89 -0
- package/src/api/projects.ts +105 -0
- package/src/api/roles.ts +123 -0
- package/src/api/runtimes.ts +62 -0
- package/src/api/scheduled-tasks.ts +203 -0
- package/src/api/sessions.ts +479 -0
- package/src/api/skills.ts +386 -0
- package/src/api/tasks.ts +457 -0
- package/src/api/telegram.ts +94 -0
- package/src/api/templates.ts +36 -0
- package/src/api/webhooks.ts +20 -0
- package/src/api/workspaces.ts +150 -0
- package/src/bridges/lark/index.ts +213 -0
- package/src/bridges/telegram/index.ts +273 -0
- package/src/bridges/telegram/polling.ts +185 -0
- package/src/chat/index.ts +86 -0
- package/src/chief/index.ts +461 -0
- package/src/core/cli.ts +333 -0
- package/src/core/db.ts +53 -0
- package/src/core/event-bus.ts +33 -0
- package/src/core/index.ts +6 -0
- package/src/core/migrations.ts +303 -0
- package/src/core/router.ts +69 -0
- package/src/core/schema.sql +232 -0
- package/src/core/server.ts +308 -0
- package/src/core/validate.ts +22 -0
- package/src/discovery/index.ts +194 -0
- package/src/gateway/adapters/telegram.ts +148 -0
- package/src/gateway/index.ts +31 -0
- package/src/gateway/manager.ts +176 -0
- package/src/gateway/types.ts +77 -0
- package/src/inbox/index.ts +500 -0
- package/src/ops/artifact-sync.ts +65 -0
- package/src/ops/autopilot.ts +338 -0
- package/src/ops/gc.ts +252 -0
- package/src/ops/index.ts +226 -0
- package/src/ops/project-serial.ts +52 -0
- package/src/ops/role-dispatch.ts +111 -0
- package/src/ops/runtime-scheduler.ts +447 -0
- package/src/ops/task-blocking.ts +65 -0
- package/src/ops/task-deps.ts +37 -0
- package/src/ops/task-workspace.ts +60 -0
- package/src/roles/index.ts +258 -0
- package/src/roles/prompt-assembler.ts +85 -0
- package/src/roles/workspace-role.ts +155 -0
- package/src/scheduler/index.ts +461 -0
- package/src/session/output-parser.ts +75 -0
- package/src/session/realtime-parser.ts +40 -0
- package/src/skills/builtin.ts +155 -0
- package/src/skills/skill-extractor.ts +452 -0
- package/src/skills/skill-md.ts +282 -0
- package/src/transport/http-api.ts +75 -0
- package/src/transport/index.ts +4 -0
- package/src/transport/local-pty.ts +119 -0
- package/src/transport/ssh.ts +176 -0
- package/src/transport/types.ts +20 -0
- package/src/workflows/index.ts +231 -0
- package/index.js +0 -1
- package/maestro-agent-0.0.1.tgz +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Long Polling Gateway
|
|
3
|
+
*
|
|
4
|
+
* 主动轮询 Telegram getUpdates API,无需 webhook、无需公网地址。
|
|
5
|
+
* 本地 NAT/防火墙后也能正常工作。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { HubContext } from "../../core/server";
|
|
9
|
+
import { getConfig } from "../../api/config";
|
|
10
|
+
import {
|
|
11
|
+
getBindingForChat,
|
|
12
|
+
handleTelegramCommand,
|
|
13
|
+
isTelegramCommand,
|
|
14
|
+
pushAgentReplyToTelegram,
|
|
15
|
+
} from "./index";
|
|
16
|
+
import { insertChatMessage, runAgentLoop } from "../../api/chat";
|
|
17
|
+
|
|
18
|
+
const TG_API = "https://api.telegram.org";
|
|
19
|
+
|
|
20
|
+
interface TelegramPollingOptions {
|
|
21
|
+
botToken: string;
|
|
22
|
+
pollingInterval?: number; // ms between polls when no updates, default 1000
|
|
23
|
+
timeout?: number; // long-poll timeout in seconds, default 30
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class TelegramPollingGateway {
|
|
27
|
+
private botToken: string;
|
|
28
|
+
private ctx: HubContext;
|
|
29
|
+
private offset = 0;
|
|
30
|
+
private running = false;
|
|
31
|
+
private timeout: number;
|
|
32
|
+
private pollingInterval: number;
|
|
33
|
+
private abortController: AbortController | null = null;
|
|
34
|
+
|
|
35
|
+
constructor(ctx: HubContext, options: TelegramPollingOptions) {
|
|
36
|
+
this.ctx = ctx;
|
|
37
|
+
this.botToken = options.botToken;
|
|
38
|
+
this.timeout = options.timeout ?? 30;
|
|
39
|
+
this.pollingInterval = options.pollingInterval ?? 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
start() {
|
|
43
|
+
if (this.running) return;
|
|
44
|
+
this.running = true;
|
|
45
|
+
console.log("[telegram-polling] Gateway started, polling for updates...");
|
|
46
|
+
this.poll();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
stop() {
|
|
50
|
+
this.running = false;
|
|
51
|
+
this.abortController?.abort();
|
|
52
|
+
console.log("[telegram-polling] Gateway stopped.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private async poll() {
|
|
56
|
+
if (!this.running) return;
|
|
57
|
+
try {
|
|
58
|
+
const updates = await this.getUpdates();
|
|
59
|
+
for (const update of updates) {
|
|
60
|
+
this.offset = update.update_id + 1;
|
|
61
|
+
await this.handleUpdate(update).catch((err) => {
|
|
62
|
+
console.error("[telegram-polling] Error handling update:", err.message);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
} catch (err: any) {
|
|
66
|
+
if (!this.running) return;
|
|
67
|
+
console.error("[telegram-polling] Poll error:", err.message);
|
|
68
|
+
await sleep(this.pollingInterval * 3);
|
|
69
|
+
}
|
|
70
|
+
if (this.running) {
|
|
71
|
+
setTimeout(() => this.poll(), 0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async getUpdates(): Promise<any[]> {
|
|
76
|
+
this.abortController = new AbortController();
|
|
77
|
+
const url = `${TG_API}/bot${this.botToken}/getUpdates?offset=${this.offset}&timeout=${this.timeout}&allowed_updates=["message","edited_message"]`;
|
|
78
|
+
const res = await fetch(url, {
|
|
79
|
+
signal: this.abortController.signal,
|
|
80
|
+
});
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
const text = await res.text().catch(() => "");
|
|
83
|
+
throw new Error(`Telegram API ${res.status}: ${text}`);
|
|
84
|
+
}
|
|
85
|
+
const data = await res.json() as any;
|
|
86
|
+
if (!data.ok) throw new Error(`Telegram error: ${data.description}`);
|
|
87
|
+
return data.result || [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async handleUpdate(update: any) {
|
|
91
|
+
const msg = update.message || update.edited_message;
|
|
92
|
+
if (!msg?.text) return;
|
|
93
|
+
|
|
94
|
+
const chatId = msg.chat?.id;
|
|
95
|
+
const text = msg.text;
|
|
96
|
+
if (!chatId) return;
|
|
97
|
+
|
|
98
|
+
// Slash command
|
|
99
|
+
if (isTelegramCommand(text)) {
|
|
100
|
+
const response = handleTelegramCommand(this.ctx.db, text, chatId);
|
|
101
|
+
await this.sendResponse(response);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Non-command: check workspace binding
|
|
106
|
+
const binding = getBindingForChat(this.ctx.db, chatId);
|
|
107
|
+
if (binding) {
|
|
108
|
+
// Bridge to workspace chat
|
|
109
|
+
const humanMsg = insertChatMessage(this.ctx, binding.workspace_id, "human", "telegram", text);
|
|
110
|
+
this.ctx.bus.publish("chat.message", {
|
|
111
|
+
workspace_id: binding.workspace_id,
|
|
112
|
+
message_id: humanMsg.id,
|
|
113
|
+
seq: humanMsg.seq,
|
|
114
|
+
sender_type: "human",
|
|
115
|
+
source: "telegram",
|
|
116
|
+
tg_chat_id: String(chatId),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Trigger agent loop (non-blocking)
|
|
120
|
+
runAgentLoop(this.ctx, binding.workspace_id).catch((err) => {
|
|
121
|
+
console.error(`[telegram-polling] agent loop error for ${binding.workspace_id}:`, err.message);
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Not bound, show help
|
|
127
|
+
const response = handleTelegramCommand(this.ctx.db, text, chatId);
|
|
128
|
+
await this.sendResponse(response);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private async sendResponse(response: any) {
|
|
132
|
+
if (!response?.payload) return;
|
|
133
|
+
await fetch(`${TG_API}/bot${this.botToken}/${response.method}`, {
|
|
134
|
+
method: "POST",
|
|
135
|
+
headers: { "Content-Type": "application/json" },
|
|
136
|
+
body: JSON.stringify(response.payload),
|
|
137
|
+
}).catch((err) => {
|
|
138
|
+
console.error("[telegram-polling] Send error:", err.message);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 启动 Telegram polling gateway(如果配置了 BOT_TOKEN)。
|
|
145
|
+
* 同时注册 agent reply push-back 监听。
|
|
146
|
+
*/
|
|
147
|
+
export function startTelegramPolling(ctx: HubContext): TelegramPollingGateway | null {
|
|
148
|
+
const botToken = getConfig(ctx, "telegram_bot_token") || process.env.MAESTRO_TELEGRAM_BOT_TOKEN;
|
|
149
|
+
if (!botToken) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const gateway = new TelegramPollingGateway(ctx, { botToken });
|
|
154
|
+
gateway.start();
|
|
155
|
+
|
|
156
|
+
// Push agent replies back to TG
|
|
157
|
+
ctx.bus.on("chat.message", async (event: any) => {
|
|
158
|
+
if (event.sender_type !== "agent") return;
|
|
159
|
+
if (event.source === "telegram") return;
|
|
160
|
+
|
|
161
|
+
const workspaceId = event.workspace_id;
|
|
162
|
+
if (!workspaceId) return;
|
|
163
|
+
|
|
164
|
+
const binding = ctx.db.query("SELECT chat_id FROM telegram_binding WHERE workspace_id = ?").get(workspaceId) as any;
|
|
165
|
+
if (!binding) return;
|
|
166
|
+
|
|
167
|
+
const msg = event.message_id
|
|
168
|
+
? ctx.db.query("SELECT content, sender_id FROM chat_message WHERE id = ?").get(event.message_id) as any
|
|
169
|
+
: null;
|
|
170
|
+
if (!msg?.content) return;
|
|
171
|
+
|
|
172
|
+
await pushAgentReplyToTelegram({
|
|
173
|
+
bot_token: botToken,
|
|
174
|
+
chat_id: binding.chat_id,
|
|
175
|
+
sender_id: msg.sender_id || "MAESTRO",
|
|
176
|
+
content: msg.content,
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return gateway;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function sleep(ms: number) {
|
|
184
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
185
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { getDb, generateId, now } from "../core/db";
|
|
2
|
+
|
|
3
|
+
export interface ChatCommandOptions {
|
|
4
|
+
hubDir: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface ChatCommandResult {
|
|
8
|
+
status: number;
|
|
9
|
+
stdout: string;
|
|
10
|
+
stderr: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function runChatCommand(args: string[], opts: ChatCommandOptions): Promise<ChatCommandResult> {
|
|
14
|
+
const jsonMode = hasFlag(args, "--json");
|
|
15
|
+
const command = args[0];
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
if (command === "send") {
|
|
19
|
+
const workspaceId = args[1];
|
|
20
|
+
if (!workspaceId) return fail("Usage: maestro chat send <workspace_id> <message>");
|
|
21
|
+
const content = args.slice(2).filter(a => !a.startsWith("--")).join(" ");
|
|
22
|
+
if (!content) return fail("Usage: maestro chat send <workspace_id> <message>");
|
|
23
|
+
|
|
24
|
+
const hubUrl = process.env.MAESTRO_HUB_URL || "http://localhost:7423";
|
|
25
|
+
const resp = await fetch(`${hubUrl}/api/chat/messages`, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: { "Content-Type": "application/json" },
|
|
28
|
+
body: JSON.stringify({ workspace_id: workspaceId, content }),
|
|
29
|
+
});
|
|
30
|
+
if (!resp.ok) {
|
|
31
|
+
const err = await resp.json().catch(() => ({ error: resp.statusText }));
|
|
32
|
+
return fail((err as any).error || `HTTP ${resp.status}`);
|
|
33
|
+
}
|
|
34
|
+
const msg = await resp.json() as any;
|
|
35
|
+
return ok(msg, jsonMode, `sent ${msg.id} (agent will respond asynchronously)`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (command === "history") {
|
|
39
|
+
const workspaceId = args[1];
|
|
40
|
+
if (!workspaceId) return fail("Usage: maestro chat history <workspace_id>");
|
|
41
|
+
const limit = Number(flagValue(args, "--limit") || "20");
|
|
42
|
+
|
|
43
|
+
const hubUrl = process.env.MAESTRO_HUB_URL || "http://localhost:7423";
|
|
44
|
+
const url = `${hubUrl}/api/chat/messages?workspace_id=${encodeURIComponent(workspaceId)}&limit=${limit}`;
|
|
45
|
+
const resp = await fetch(url);
|
|
46
|
+
if (!resp.ok) return fail(`HTTP ${resp.status}`);
|
|
47
|
+
const messages = await resp.json() as any[];
|
|
48
|
+
return ok(messages, jsonMode, renderMessages(messages));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return fail(`Unknown command: ${command || ""}\nUsage: maestro chat <send|history> ...`);
|
|
52
|
+
} catch (err: any) {
|
|
53
|
+
return fail(err.message);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function renderMessages(messages: any[]): string {
|
|
58
|
+
if (!messages || messages.length === 0) return "no messages";
|
|
59
|
+
return messages.map((m) => {
|
|
60
|
+
const time = new Date(m.created_at).toLocaleTimeString();
|
|
61
|
+
const role = m.sender_type === "human" ? "you" : "agent";
|
|
62
|
+
return `[${time}] ${role}: ${m.content}`;
|
|
63
|
+
}).join("\n");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function flagValue(args: string[], flag: string): string | undefined {
|
|
67
|
+
const index = args.indexOf(flag);
|
|
68
|
+
if (index === -1) return undefined;
|
|
69
|
+
return args[index + 1];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function hasFlag(args: string[], flag: string): boolean {
|
|
73
|
+
return args.includes(flag);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function ok(data: unknown, jsonMode: boolean, text: string): ChatCommandResult {
|
|
77
|
+
return {
|
|
78
|
+
status: 0,
|
|
79
|
+
stdout: jsonMode ? `${JSON.stringify(data)}\n` : `${text}\n`,
|
|
80
|
+
stderr: "",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function fail(message: string): ChatCommandResult {
|
|
85
|
+
return { status: 1, stdout: "", stderr: `${message}\n` };
|
|
86
|
+
}
|