@controlflow-ai/daemon 0.1.2 → 0.1.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 +54 -6
- package/package.json +3 -1
- package/src/agent-avatar.ts +30 -0
- package/src/agent-key.ts +28 -0
- package/src/agent-permissions.ts +359 -0
- package/src/agent-runtime.ts +795 -28
- package/src/agent-workspace.ts +183 -0
- package/src/app.ts +1970 -79
- package/src/args.ts +54 -7
- package/src/cli.ts +873 -14
- package/src/client.ts +472 -10
- package/src/coco.ts +9 -40
- package/src/codex.ts +33 -5
- package/src/config.ts +28 -4
- package/src/console.ts +230 -20
- package/src/daemon-client.ts +116 -3
- package/src/daemon.ts +936 -98
- package/src/db.ts +3128 -122
- package/src/delivery-ws.ts +269 -0
- package/src/format.ts +4 -1
- package/src/lark/cli.ts +3 -3
- package/src/lark/event-router.ts +60 -4
- package/src/lark/inbound-events.ts +156 -3
- package/src/lark/server-integration.ts +659 -111
- package/src/lark/ws-daemon.ts +136 -10
- package/src/local-api.ts +545 -15
- package/src/local-auth.ts +33 -1
- package/src/message-attachments.ts +71 -0
- package/src/messaging-cli.ts +741 -0
- package/src/messaging-status.ts +669 -0
- package/src/migrations/024_agents_model.ts +10 -0
- package/src/migrations/025_room_archive.ts +44 -0
- package/src/migrations/026_project_archive.ts +44 -0
- package/src/migrations/027_agent_permission_profiles.ts +16 -0
- package/src/migrations/028_lark_websocket_restart_state.ts +16 -0
- package/src/migrations/029_held_message_drafts.ts +32 -0
- package/src/migrations/030_agent_room_read_state.ts +25 -0
- package/src/migrations/031_room_tasks.ts +29 -0
- package/src/migrations/032_room_reminders.ts +29 -0
- package/src/migrations/033_room_saved_messages.ts +25 -0
- package/src/migrations/034_agent_activity_events.ts +27 -0
- package/src/migrations/035_agent_avatars.ts +17 -0
- package/src/migrations/036_project_agent_defaults.ts +21 -0
- package/src/migrations/037_message_attachments.ts +36 -0
- package/src/migrations/038_agent_activity_room_scope.ts +64 -0
- package/src/migrations/039_message_attachments_path.ts +34 -0
- package/src/migrations/040_message_attachments_file_schema.ts +80 -0
- package/src/migrations/041_room_system_events.ts +30 -0
- package/src/migrations/042_message_attachment_file_kind.ts +52 -0
- package/src/migrations/043_room_mode_skill_registry.ts +92 -0
- package/src/migrations/044_workflow_runtime.ts +69 -0
- package/src/migrations/045_skill_repository_ownership.ts +64 -0
- package/src/migrations.ts +69 -1
- package/src/neeko.ts +40 -4
- package/src/runtime-env.ts +179 -0
- package/src/runtime-registry.ts +83 -13
- package/src/server.ts +244 -4
- package/src/token-file.ts +13 -6
- package/src/types.ts +362 -0
- package/src/workflow-runtime.ts +275 -0
- package/src/web.ts +0 -904
package/src/lark/ws-daemon.ts
CHANGED
|
@@ -10,13 +10,22 @@ export type LarkEventCallback = (event: {
|
|
|
10
10
|
storeResult: StoreInboundEventResult;
|
|
11
11
|
}) => void | Promise<void>;
|
|
12
12
|
|
|
13
|
+
export type LarkWebSocketLifecycleCallback = (event: {
|
|
14
|
+
appId: string;
|
|
15
|
+
type: 'ready' | 'reconnecting' | 'reconnected' | 'error';
|
|
16
|
+
error?: string;
|
|
17
|
+
}) => void;
|
|
18
|
+
|
|
13
19
|
export interface StartLarkDaemonOptions {
|
|
14
20
|
appId: string;
|
|
15
21
|
appSecret: string;
|
|
16
22
|
db: Database;
|
|
17
23
|
onEvent?: LarkEventCallback;
|
|
24
|
+
onLifecycle?: LarkWebSocketLifecycleCallback;
|
|
18
25
|
logger?: Partial<Pick<Console, 'info' | 'warn' | 'error'>>;
|
|
19
26
|
loggerLevel?: Lark.LoggerLevel;
|
|
27
|
+
handshakeTimeoutMs?: number;
|
|
28
|
+
pingTimeoutSec?: number;
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
export interface LarkDaemonHandle {
|
|
@@ -26,6 +35,44 @@ export interface LarkDaemonHandle {
|
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
const SUPPORTED_EVENTS = ['im.message.receive_v1', 'card.action.trigger'] as const;
|
|
38
|
+
const silentLarkLogger = {
|
|
39
|
+
error() {},
|
|
40
|
+
warn() {},
|
|
41
|
+
info() {},
|
|
42
|
+
debug() {},
|
|
43
|
+
trace() {},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function nestedString(value: unknown, path: string[]): string | null {
|
|
47
|
+
let current = value;
|
|
48
|
+
for (const key of path) {
|
|
49
|
+
if (!current || typeof current !== 'object') return null;
|
|
50
|
+
current = (current as Record<string, unknown>)[key];
|
|
51
|
+
}
|
|
52
|
+
return typeof current === 'string' && current.length > 0 ? current : null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function stableLarkEventId(eventName: string, data: unknown): string | null {
|
|
56
|
+
const headerId = nestedString(data, ['header', 'event_id'])
|
|
57
|
+
?? nestedString(data, ['event_id']);
|
|
58
|
+
if (headerId) return headerId;
|
|
59
|
+
const derivedId = nestedString(data, ['message', 'message_id'])
|
|
60
|
+
?? nestedString(data, ['message', 'open_message_id'])
|
|
61
|
+
?? nestedString(data, ['open_message_id']);
|
|
62
|
+
if (derivedId) return `${eventName}:${derivedId}`;
|
|
63
|
+
const operatorId = nestedString(data, ['operator', 'operator_id', 'open_id']);
|
|
64
|
+
if (operatorId) return `${eventName}:${operatorId}:${nestedString(data, ['token']) ?? ''}`;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function buildLarkEventRawBody(eventName: string, data: unknown): string {
|
|
69
|
+
const eventId = stableLarkEventId(eventName, data);
|
|
70
|
+
return JSON.stringify({
|
|
71
|
+
schema: '2.0',
|
|
72
|
+
header: eventId ? { event_id: eventId, event_type: eventName } : { event_type: eventName },
|
|
73
|
+
event: data,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
29
76
|
|
|
30
77
|
/**
|
|
31
78
|
* Start a Lark WSClient that subscribes to im.message.receive_v1 +
|
|
@@ -45,7 +92,7 @@ export function startLarkDaemon(options: StartLarkDaemonOptions): LarkDaemonHand
|
|
|
45
92
|
const handlers: Record<string, (data: unknown) => Promise<unknown>> = {};
|
|
46
93
|
for (const eventName of SUPPORTED_EVENTS) {
|
|
47
94
|
handlers[eventName] = async (data) => {
|
|
48
|
-
const rawBody =
|
|
95
|
+
const rawBody = buildLarkEventRawBody(eventName, data);
|
|
49
96
|
try {
|
|
50
97
|
const storeResult = await storeInboundEvent(options.db, { appId: options.appId, rawBody });
|
|
51
98
|
if (storeResult.duplicate) {
|
|
@@ -75,6 +122,24 @@ export function startLarkDaemon(options: StartLarkDaemonOptions): LarkDaemonHand
|
|
|
75
122
|
appId: options.appId,
|
|
76
123
|
appSecret: options.appSecret,
|
|
77
124
|
loggerLevel: options.loggerLevel ?? Lark.LoggerLevel.warn,
|
|
125
|
+
handshakeTimeoutMs: options.handshakeTimeoutMs ?? 15_000,
|
|
126
|
+
wsConfig: { pingTimeout: options.pingTimeoutSec ?? 10 },
|
|
127
|
+
onReady: () => {
|
|
128
|
+
options.onLifecycle?.({ appId: options.appId, type: 'ready' });
|
|
129
|
+
log.info?.(`[lark/${options.appId}] WSClient ready`);
|
|
130
|
+
},
|
|
131
|
+
onReconnecting: () => {
|
|
132
|
+
options.onLifecycle?.({ appId: options.appId, type: 'reconnecting' });
|
|
133
|
+
log.warn?.(`[lark/${options.appId}] WSClient reconnecting`);
|
|
134
|
+
},
|
|
135
|
+
onReconnected: () => {
|
|
136
|
+
options.onLifecycle?.({ appId: options.appId, type: 'reconnected' });
|
|
137
|
+
log.info?.(`[lark/${options.appId}] WSClient reconnected`);
|
|
138
|
+
},
|
|
139
|
+
onError: (err) => {
|
|
140
|
+
options.onLifecycle?.({ appId: options.appId, type: 'error', error: err.message });
|
|
141
|
+
log.error?.(`[lark/${options.appId}] WSClient error: ${err.message}`);
|
|
142
|
+
},
|
|
78
143
|
});
|
|
79
144
|
|
|
80
145
|
wsClient.start({ eventDispatcher });
|
|
@@ -103,35 +168,96 @@ export function createLarkApiClient(appId: string, appSecret: string): Lark.Clie
|
|
|
103
168
|
return new Lark.Client({
|
|
104
169
|
appId,
|
|
105
170
|
appSecret,
|
|
106
|
-
loggerLevel: Lark.LoggerLevel.
|
|
171
|
+
loggerLevel: Lark.LoggerLevel.fatal,
|
|
172
|
+
logger: silentLarkLogger,
|
|
107
173
|
});
|
|
108
174
|
}
|
|
109
175
|
|
|
110
|
-
export
|
|
176
|
+
export type LarkReceiveIdType = 'chat_id' | 'open_id' | 'union_id' | 'email' | 'user_id';
|
|
177
|
+
|
|
178
|
+
export interface LarkMentionTarget {
|
|
179
|
+
openId: string;
|
|
180
|
+
displayName?: string | null;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface SendMessageBaseInput {
|
|
111
184
|
client: Lark.Client;
|
|
112
|
-
receiveIdType:
|
|
185
|
+
receiveIdType: LarkReceiveIdType;
|
|
113
186
|
receiveId: string;
|
|
114
187
|
text: string;
|
|
188
|
+
mention?: LarkMentionTarget | null;
|
|
115
189
|
}
|
|
116
190
|
|
|
191
|
+
export interface SendTextMessageInput extends SendMessageBaseInput {}
|
|
192
|
+
|
|
193
|
+
export interface SendCardMessageInput extends SendMessageBaseInput {}
|
|
194
|
+
|
|
117
195
|
export interface SendTextMessageResult {
|
|
118
196
|
messageId?: string;
|
|
119
197
|
raw: unknown;
|
|
120
198
|
}
|
|
121
199
|
|
|
122
|
-
export
|
|
200
|
+
export interface LarkMessageCreatePayload {
|
|
201
|
+
receive_id: string;
|
|
202
|
+
msg_type: 'text' | 'interactive';
|
|
203
|
+
content: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function escapeLarkText(value: string): string {
|
|
207
|
+
return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function larkMentionSuffix(mention: LarkMentionTarget | null | undefined, tag: 'text' | 'card'): string {
|
|
211
|
+
if (!mention?.openId) return '';
|
|
212
|
+
const idAttribute = tag === 'text' ? 'user_id' : 'id';
|
|
213
|
+
return `\n\n<at ${idAttribute}="${mention.openId}">${escapeLarkText(mention.displayName ?? '')}</at>`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function larkMessageBody(input: Pick<SendMessageBaseInput, 'text' | 'mention'>, tag: 'text' | 'card'): string {
|
|
217
|
+
return `${input.text}${larkMentionSuffix(input.mention, tag)}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function buildLarkTextMessagePayload(input: Pick<SendTextMessageInput, 'receiveId' | 'text' | 'mention'>): LarkMessageCreatePayload {
|
|
221
|
+
return {
|
|
222
|
+
receive_id: input.receiveId,
|
|
223
|
+
msg_type: 'text',
|
|
224
|
+
content: JSON.stringify({ text: larkMessageBody(input, 'text') }),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function buildLarkMentionCardPayload(input: Pick<SendCardMessageInput, 'receiveId' | 'text' | 'mention'>): LarkMessageCreatePayload {
|
|
229
|
+
return {
|
|
230
|
+
receive_id: input.receiveId,
|
|
231
|
+
msg_type: 'interactive',
|
|
232
|
+
content: JSON.stringify({
|
|
233
|
+
config: { wide_screen_mode: true },
|
|
234
|
+
elements: [
|
|
235
|
+
{
|
|
236
|
+
tag: 'markdown',
|
|
237
|
+
content: larkMessageBody(input, 'card'),
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
}),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function createLarkMessage(input: SendMessageBaseInput, payload: LarkMessageCreatePayload): Promise<SendTextMessageResult> {
|
|
123
245
|
const response = await input.client.im.message.create({
|
|
124
246
|
params: { receive_id_type: input.receiveIdType },
|
|
125
|
-
data:
|
|
126
|
-
receive_id: input.receiveId,
|
|
127
|
-
msg_type: 'text',
|
|
128
|
-
content: JSON.stringify({ text: input.text }),
|
|
129
|
-
},
|
|
247
|
+
data: payload,
|
|
130
248
|
});
|
|
131
249
|
const messageId = (response?.data as { message_id?: string } | undefined)?.message_id;
|
|
132
250
|
return { messageId, raw: response };
|
|
133
251
|
}
|
|
134
252
|
|
|
253
|
+
export async function sendTextMessage(input: SendTextMessageInput): Promise<SendTextMessageResult> {
|
|
254
|
+
return createLarkMessage(input, buildLarkTextMessagePayload(input));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export async function sendCardMessage(input: SendCardMessageInput): Promise<SendTextMessageResult> {
|
|
258
|
+
return createLarkMessage(input, buildLarkMentionCardPayload(input));
|
|
259
|
+
}
|
|
260
|
+
|
|
135
261
|
export interface AddMessageReactionInput {
|
|
136
262
|
client: Lark.Client;
|
|
137
263
|
messageId: string;
|