maestro-agent 0.0.1 → 0.0.2
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/assets/Connections-DV2Kql1Z.js +1 -0
- package/dist/web/assets/GanttView-CCT_rFpY.js +39 -0
- package/dist/web/assets/Home-BFbUIh2z.js +1 -0
- package/dist/web/assets/HooksCrons-ASM5-jDm.js +1 -0
- package/dist/web/assets/ProjectDetail-KZZi6IAd.js +1 -0
- package/dist/web/assets/Roles-KQ94PG3H.js +4 -0
- package/dist/web/assets/ScheduledTasks-CdJHJpEV.js +1 -0
- package/dist/web/assets/Settings-CTflMta-.js +1 -0
- package/dist/web/assets/Skills-D09W1mwX.js +2 -0
- package/dist/web/assets/Wizard-CW6B0wc3.js +1 -0
- package/dist/web/assets/WorkspaceChat-CthETL_A.js +1 -0
- package/dist/web/assets/WorkspaceDashboard-DTAesQuT.js +1 -0
- package/dist/web/assets/WorkspaceNew-Em4msIKn.js +1 -0
- package/dist/web/assets/WorkspaceProjects-Dxg2BpQy.js +1 -0
- package/dist/web/assets/WorkspaceTasks-C20mnnkP.js +1 -0
- package/dist/web/assets/index-B1k33vcR.js +11 -0
- package/dist/web/assets/index-Bk2hHz7P.css +1 -0
- package/dist/web/assets/index-Ddy5AJwx.js +61 -0
- package/dist/web/assets/useEventStream-DTID465I.js +1 -0
- package/dist/web/index.html +13 -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,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway 入口 — 初始化并启动所有配置的平台 Adapter
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { HubContext } from "../core/server";
|
|
6
|
+
import { getConfig } from "../api/config";
|
|
7
|
+
import { GatewayManager } from "./manager";
|
|
8
|
+
import { TelegramAdapter } from "./adapters/telegram";
|
|
9
|
+
|
|
10
|
+
export async function startGateway(ctx: HubContext): Promise<GatewayManager | null> {
|
|
11
|
+
// 优先从 config 表读取,环境变量作为 fallback
|
|
12
|
+
const botToken = getConfig(ctx, "telegram_bot_token") || process.env.MAESTRO_TELEGRAM_BOT_TOKEN;
|
|
13
|
+
|
|
14
|
+
// 如果没有配置任何平台 token,不启动网关
|
|
15
|
+
if (!botToken) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const manager = new GatewayManager(ctx);
|
|
20
|
+
|
|
21
|
+
// Telegram Adapter
|
|
22
|
+
const telegram = new TelegramAdapter({
|
|
23
|
+
botToken,
|
|
24
|
+
pollingTimeout: 30,
|
|
25
|
+
onMessage: manager.getMessageHandler(),
|
|
26
|
+
});
|
|
27
|
+
manager.register(telegram);
|
|
28
|
+
|
|
29
|
+
await manager.connectAll();
|
|
30
|
+
return manager;
|
|
31
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GatewayManager — 统一消息网关调度层
|
|
3
|
+
*
|
|
4
|
+
* 管理所有平台 Adapter 的生命周期,统一消息路由。
|
|
5
|
+
* 参考 Hermes 的 gateway/run.py 和 OpenClaw 的 server-plugins.ts。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { handleTelegramCommand, isTelegramCommand } from "../bridges/telegram";
|
|
9
|
+
import type { HubContext } from "../core/server";
|
|
10
|
+
import type { InboundMessage, MessageHandler, PlatformAdapter } from "./types";
|
|
11
|
+
|
|
12
|
+
export class GatewayManager {
|
|
13
|
+
private adapters: Map<string, PlatformAdapter> = new Map();
|
|
14
|
+
private ctx: HubContext;
|
|
15
|
+
private messageHandler: MessageHandler;
|
|
16
|
+
|
|
17
|
+
constructor(ctx: HubContext) {
|
|
18
|
+
this.ctx = ctx;
|
|
19
|
+
this.messageHandler = this.handleInboundMessage.bind(this);
|
|
20
|
+
|
|
21
|
+
// Agent 回复推送:监听 chat.message 事件,路由到对应平台
|
|
22
|
+
ctx.bus.on("chat.message", (event: any) => {
|
|
23
|
+
this.handleAgentReply(event).catch((err) => {
|
|
24
|
+
console.error("[gateway] agent reply push error:", err.message);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 注册一个平台 Adapter */
|
|
30
|
+
register(adapter: PlatformAdapter) {
|
|
31
|
+
this.adapters.set(adapter.platform, adapter);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 启动所有已注册的 Adapter */
|
|
35
|
+
async connectAll(): Promise<void> {
|
|
36
|
+
for (const [platform, adapter] of this.adapters) {
|
|
37
|
+
try {
|
|
38
|
+
const ok = await adapter.connect();
|
|
39
|
+
if (ok) {
|
|
40
|
+
console.log(`[gateway] ✅ ${platform} connected`);
|
|
41
|
+
} else {
|
|
42
|
+
console.warn(`[gateway] ⚠️ ${platform} connection returned false`);
|
|
43
|
+
}
|
|
44
|
+
} catch (err: any) {
|
|
45
|
+
console.error(`[gateway] ❌ ${platform} connect failed:`, err.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** 停止所有 Adapter */
|
|
51
|
+
async disconnectAll(): Promise<void> {
|
|
52
|
+
for (const [platform, adapter] of this.adapters) {
|
|
53
|
+
try {
|
|
54
|
+
await adapter.disconnect();
|
|
55
|
+
console.log(`[gateway] ${platform} disconnected`);
|
|
56
|
+
} catch (err: any) {
|
|
57
|
+
console.error(`[gateway] ${platform} disconnect error:`, err.message);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** 获取消息处理回调(供 Adapter 内部调用) */
|
|
63
|
+
getMessageHandler(): MessageHandler {
|
|
64
|
+
return this.messageHandler;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** 获取指定平台的 Adapter */
|
|
68
|
+
getAdapter(platform: string): PlatformAdapter | undefined {
|
|
69
|
+
return this.adapters.get(platform);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 统一入站消息处理 */
|
|
73
|
+
private async handleInboundMessage(msg: InboundMessage): Promise<void> {
|
|
74
|
+
const { platform, chatId, text } = msg;
|
|
75
|
+
|
|
76
|
+
// Telegram 特殊处理:slash 命令
|
|
77
|
+
if (platform === "telegram" && isTelegramCommand(text)) {
|
|
78
|
+
const response = handleTelegramCommand(this.ctx.db, text, chatId);
|
|
79
|
+
const adapter = this.adapters.get("telegram");
|
|
80
|
+
if (adapter && response?.payload?.text) {
|
|
81
|
+
await adapter.send(chatId, response.payload.text, { parseMode: "markdown" });
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 查找 binding(chat_id → workspace_id)
|
|
87
|
+
const binding = this.ctx.db.query(
|
|
88
|
+
"SELECT workspace_id FROM telegram_binding WHERE chat_id = ?"
|
|
89
|
+
).get(chatId) as any;
|
|
90
|
+
|
|
91
|
+
if (!binding) {
|
|
92
|
+
// 未绑定,发送帮助信息
|
|
93
|
+
const adapter = this.adapters.get(platform);
|
|
94
|
+
if (adapter) {
|
|
95
|
+
await adapter.send(chatId, "⚠️ This chat is not bound to any workspace. Use /bind <workspace_name> to link it.");
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 桥接到 workspace chat (lazy import to avoid circular deps)
|
|
101
|
+
const { insertChatMessage, runAgentLoop } = await import("../api/chat");
|
|
102
|
+
const humanMsg = insertChatMessage(this.ctx, binding.workspace_id, "human", platform, text);
|
|
103
|
+
this.ctx.bus.publish("chat.message", {
|
|
104
|
+
workspace_id: binding.workspace_id,
|
|
105
|
+
message_id: humanMsg.id,
|
|
106
|
+
seq: humanMsg.seq,
|
|
107
|
+
sender_type: "human",
|
|
108
|
+
source: platform,
|
|
109
|
+
tg_chat_id: chatId,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// 发送 typing 指示器
|
|
113
|
+
const adapter = this.adapters.get(platform);
|
|
114
|
+
adapter?.sendTyping(chatId).catch(() => {});
|
|
115
|
+
|
|
116
|
+
// 触发 agent loop
|
|
117
|
+
runAgentLoop(this.ctx, binding.workspace_id).catch((err) => {
|
|
118
|
+
console.error(`[gateway] agent loop error for ${binding.workspace_id}:`, err.message);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Agent 回复推送回对应平台 */
|
|
123
|
+
private async handleAgentReply(event: any) {
|
|
124
|
+
if (event.sender_type !== "agent") return;
|
|
125
|
+
if (event.source && this.adapters.has(event.source)) return; // 避免回声
|
|
126
|
+
|
|
127
|
+
const workspaceId = event.workspace_id;
|
|
128
|
+
if (!workspaceId) return;
|
|
129
|
+
|
|
130
|
+
// 查找该 workspace 绑定的所有 chat
|
|
131
|
+
const bindings = this.ctx.db.query(
|
|
132
|
+
"SELECT chat_id FROM telegram_binding WHERE workspace_id = ?"
|
|
133
|
+
).all(workspaceId) as any[];
|
|
134
|
+
if (!bindings?.length) return;
|
|
135
|
+
|
|
136
|
+
// 获取消息内容
|
|
137
|
+
const msg = event.message_id
|
|
138
|
+
? this.ctx.db.query("SELECT content, sender_id FROM chat_message WHERE id = ?").get(event.message_id) as any
|
|
139
|
+
: null;
|
|
140
|
+
if (!msg?.content) return;
|
|
141
|
+
|
|
142
|
+
const formattedText = `🤖 *${msg.sender_id || "MAESTRO"}*\n${msg.content}`;
|
|
143
|
+
|
|
144
|
+
// 推送到所有绑定的 chat(目前只有 telegram,后续扩展)
|
|
145
|
+
for (const binding of bindings) {
|
|
146
|
+
// 根据 binding 确定平台(目前 telegram_binding 表只存 telegram)
|
|
147
|
+
const adapter = this.adapters.get("telegram");
|
|
148
|
+
if (!adapter) continue;
|
|
149
|
+
|
|
150
|
+
// 消息分片
|
|
151
|
+
const chunks = chunkText(formattedText, adapter.textChunkLimit);
|
|
152
|
+
for (const chunk of chunks) {
|
|
153
|
+
await adapter.send(binding.chat_id, chunk, { parseMode: "markdown" });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** 消息分片:按 limit 切分长文本 */
|
|
160
|
+
export function chunkText(text: string, limit: number): string[] {
|
|
161
|
+
if (text.length <= limit) return [text];
|
|
162
|
+
const chunks: string[] = [];
|
|
163
|
+
let remaining = text;
|
|
164
|
+
while (remaining.length > 0) {
|
|
165
|
+
if (remaining.length <= limit) {
|
|
166
|
+
chunks.push(remaining);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
// 尝试在换行符处切分
|
|
170
|
+
let splitAt = remaining.lastIndexOf("\n", limit);
|
|
171
|
+
if (splitAt < limit * 0.5) splitAt = limit; // 没有合适的换行,硬切
|
|
172
|
+
chunks.push(remaining.slice(0, splitAt));
|
|
173
|
+
remaining = remaining.slice(splitAt);
|
|
174
|
+
}
|
|
175
|
+
return chunks;
|
|
176
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gateway Platform Adapter 统一接口
|
|
3
|
+
*
|
|
4
|
+
* 参考 Hermes (BasePlatformAdapter) 和 OpenClaw (ChannelOutboundAdapter) 的设计。
|
|
5
|
+
* 所有消息平台(Telegram、Discord、Slack、飞书等)实现此接口。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface SendResult {
|
|
9
|
+
ok: boolean;
|
|
10
|
+
messageId?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface InboundMessage {
|
|
15
|
+
platform: string;
|
|
16
|
+
chatId: string;
|
|
17
|
+
text: string;
|
|
18
|
+
senderId?: string;
|
|
19
|
+
senderName?: string;
|
|
20
|
+
threadId?: string;
|
|
21
|
+
raw?: unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SendOptions {
|
|
25
|
+
parseMode?: "markdown" | "html" | "plain";
|
|
26
|
+
threadId?: string;
|
|
27
|
+
replyToMessageId?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 每个平台 Adapter 必须实现的接口
|
|
32
|
+
*/
|
|
33
|
+
export interface PlatformAdapter {
|
|
34
|
+
/** 平台标识 */
|
|
35
|
+
readonly platform: string;
|
|
36
|
+
|
|
37
|
+
/** 单条消息最大字符数(超过自动分片) */
|
|
38
|
+
readonly textChunkLimit: number;
|
|
39
|
+
|
|
40
|
+
/** 连接到平台,开始监听消息 */
|
|
41
|
+
connect(): Promise<boolean>;
|
|
42
|
+
|
|
43
|
+
/** 断开连接,停止监听 */
|
|
44
|
+
disconnect(): Promise<void>;
|
|
45
|
+
|
|
46
|
+
/** 发送文本消息 */
|
|
47
|
+
send(chatId: string, text: string, options?: SendOptions): Promise<SendResult>;
|
|
48
|
+
|
|
49
|
+
/** 发送 typing 指示器 */
|
|
50
|
+
sendTyping(chatId: string): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adapter 收到消息后的回调
|
|
55
|
+
*/
|
|
56
|
+
export type MessageHandler = (msg: InboundMessage) => Promise<void>;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Gateway 配置
|
|
60
|
+
*/
|
|
61
|
+
export interface GatewayConfig {
|
|
62
|
+
telegram?: {
|
|
63
|
+
botToken: string;
|
|
64
|
+
pollingTimeout?: number;
|
|
65
|
+
};
|
|
66
|
+
discord?: {
|
|
67
|
+
botToken: string;
|
|
68
|
+
};
|
|
69
|
+
slack?: {
|
|
70
|
+
appToken: string;
|
|
71
|
+
botToken: string;
|
|
72
|
+
};
|
|
73
|
+
feishu?: {
|
|
74
|
+
appId: string;
|
|
75
|
+
appSecret: string;
|
|
76
|
+
};
|
|
77
|
+
}
|