openclaw-remote 0.3.1 → 0.4.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/dist/index.js +68 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2795,6 +2795,35 @@ async function getTeam(account) {
|
|
|
2795
2795
|
|
|
2796
2796
|
// src/realtime-listener.ts
|
|
2797
2797
|
import { createClient } from "@supabase/supabase-js";
|
|
2798
|
+
var leaderTimers = /* @__PURE__ */ new Map();
|
|
2799
|
+
var LEADER_DEBOUNCE_MS = 6e4;
|
|
2800
|
+
var leaderRolesCache = [];
|
|
2801
|
+
var leaderRolesCacheTime = 0;
|
|
2802
|
+
var LEADER_CACHE_TTL = 5 * 6e4;
|
|
2803
|
+
async function fetchLeaderRoles(supabase, log) {
|
|
2804
|
+
const now = Date.now();
|
|
2805
|
+
if (now - leaderRolesCacheTime < LEADER_CACHE_TTL && leaderRolesCache.length > 0) {
|
|
2806
|
+
return leaderRolesCache;
|
|
2807
|
+
}
|
|
2808
|
+
const { data: roles } = await supabase.from("project_roles").select("id").eq("is_leader", true);
|
|
2809
|
+
if (!roles || roles.length === 0) {
|
|
2810
|
+
leaderRolesCache = [];
|
|
2811
|
+
leaderRolesCacheTime = now;
|
|
2812
|
+
return [];
|
|
2813
|
+
}
|
|
2814
|
+
const roleIds = roles.map((r) => r.id);
|
|
2815
|
+
const { data: assignments } = await supabase.from("agent_role_assignments").select("project_role_id, agent_id").in("project_role_id", roleIds);
|
|
2816
|
+
const map = /* @__PURE__ */ new Map();
|
|
2817
|
+
for (const a of assignments ?? []) {
|
|
2818
|
+
const list = map.get(a.project_role_id) || [];
|
|
2819
|
+
list.push(a.agent_id);
|
|
2820
|
+
map.set(a.project_role_id, list);
|
|
2821
|
+
}
|
|
2822
|
+
leaderRolesCache = roleIds.map((id) => ({ roleId: id, agentIds: map.get(id) || [] }));
|
|
2823
|
+
leaderRolesCacheTime = now;
|
|
2824
|
+
log?.info?.(`Leader roles cache refreshed: ${leaderRolesCache.length} leader role(s)`);
|
|
2825
|
+
return leaderRolesCache;
|
|
2826
|
+
}
|
|
2798
2827
|
function connectRealtime(account, onEvent, onError, log) {
|
|
2799
2828
|
const { supabaseUrl, supabaseKey } = account;
|
|
2800
2829
|
if (!supabaseUrl || !supabaseKey) {
|
|
@@ -2819,6 +2848,7 @@ function connectRealtime(account, onEvent, onError, log) {
|
|
|
2819
2848
|
const row = payload.new;
|
|
2820
2849
|
if (row.agent_author_id && !row.author_id) {
|
|
2821
2850
|
log?.info?.(`Skipping agent-authored comment on task ${row.task_id} (echo suppression)`);
|
|
2851
|
+
scheduleLeaderNotification(row.task_id, row.agent_author_id, supabase, log);
|
|
2822
2852
|
return;
|
|
2823
2853
|
}
|
|
2824
2854
|
const event = {
|
|
@@ -2854,11 +2884,49 @@ function connectRealtime(account, onEvent, onError, log) {
|
|
|
2854
2884
|
return {
|
|
2855
2885
|
unsubscribe: async () => {
|
|
2856
2886
|
log?.info?.("Unsubscribing from Supabase Realtime...");
|
|
2887
|
+
for (const timer of leaderTimers.values()) clearTimeout(timer);
|
|
2888
|
+
leaderTimers.clear();
|
|
2857
2889
|
await supabase.removeChannel(channel);
|
|
2858
2890
|
log?.info?.("Realtime listener shut down cleanly.");
|
|
2859
2891
|
}
|
|
2860
2892
|
};
|
|
2861
2893
|
}
|
|
2894
|
+
async function scheduleLeaderNotification(taskId, commentingAgentId, supabase, log) {
|
|
2895
|
+
const existing = leaderTimers.get(taskId);
|
|
2896
|
+
if (existing) clearTimeout(existing);
|
|
2897
|
+
const timer = setTimeout(async () => {
|
|
2898
|
+
leaderTimers.delete(taskId);
|
|
2899
|
+
try {
|
|
2900
|
+
const leaders = await fetchLeaderRoles(supabase, log);
|
|
2901
|
+
if (leaders.length === 0) return;
|
|
2902
|
+
const { data: task } = await supabase.from("tasks").select("id, title, assigned_role_id").eq("id", taskId).single();
|
|
2903
|
+
if (!task) return;
|
|
2904
|
+
for (const leader of leaders) {
|
|
2905
|
+
if (task.assigned_role_id === leader.roleId) {
|
|
2906
|
+
log?.info?.(`Skipping leader notification for task ${taskId} \u2014 assigned to leader role ${leader.roleId}`);
|
|
2907
|
+
continue;
|
|
2908
|
+
}
|
|
2909
|
+
if (leader.agentIds.includes(commentingAgentId)) continue;
|
|
2910
|
+
if (leader.agentIds.length === 0) continue;
|
|
2911
|
+
const { error } = await supabase.from("task_comments").insert({
|
|
2912
|
+
task_id: taskId,
|
|
2913
|
+
author_id: null,
|
|
2914
|
+
agent_author_id: null,
|
|
2915
|
+
body: `\u{1F440} Activity on this task \u2014 leader review needed`
|
|
2916
|
+
});
|
|
2917
|
+
if (error) {
|
|
2918
|
+
log?.warn?.(`Failed to insert leader notification for task ${taskId}: ${error.message}`);
|
|
2919
|
+
} else {
|
|
2920
|
+
log?.info?.(`Leader notification sent for task ${taskId}`);
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
} catch (err) {
|
|
2924
|
+
log?.warn?.(`Leader notification error for task ${taskId}: ${err}`);
|
|
2925
|
+
}
|
|
2926
|
+
}, LEADER_DEBOUNCE_MS);
|
|
2927
|
+
leaderTimers.set(taskId, timer);
|
|
2928
|
+
log?.info?.(`Leader debounce timer set for task ${taskId} (60s)`);
|
|
2929
|
+
}
|
|
2862
2930
|
|
|
2863
2931
|
// src/event-formatter.ts
|
|
2864
2932
|
function formatEvent(event) {
|