@ai-setting/roy-agent-cli 1.7.57 → 1.7.58
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/bin/roy-agent.js +315 -1
- package/dist/index.js +315 -1
- package/dist/roy-agent-linux-x64/bin/roy-agent +2 -2
- package/package.json +1 -1
package/dist/bin/roy-agent.js
CHANGED
|
@@ -9639,7 +9639,7 @@ class CliQuietModeService {
|
|
|
9639
9639
|
// package.json
|
|
9640
9640
|
var package_default = {
|
|
9641
9641
|
name: "@ai-setting/roy-agent-cli",
|
|
9642
|
-
version: "1.7.
|
|
9642
|
+
version: "1.7.58",
|
|
9643
9643
|
type: "module",
|
|
9644
9644
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9645
9645
|
main: "./dist/index.js",
|
|
@@ -11785,6 +11785,262 @@ import {
|
|
|
11785
11785
|
} from "@ai-setting/roy-agent-core/env";
|
|
11786
11786
|
import { TracedAs as TracedAs5 } from "@ai-setting/roy-agent-core";
|
|
11787
11787
|
import { notifyReactFailure as notifyReactFailure2 } from "@ai-setting/roy-agent-core/env/error-reply";
|
|
11788
|
+
|
|
11789
|
+
// src/commands/shared/telemetry-notice.ts
|
|
11790
|
+
import { spawn } from "node:child_process";
|
|
11791
|
+
import { normalizeLarkAtMentions } from "@ai-setting/roy-agent-core/env/lark-mention-utils";
|
|
11792
|
+
import { deriveIdempotencyKey } from "@ai-setting/roy-agent-core/env/event-source/idempotency-key";
|
|
11793
|
+
var TELEMETRY_EVENT_TYPES = new Set([
|
|
11794
|
+
"task.background.started",
|
|
11795
|
+
"task.background.progress"
|
|
11796
|
+
]);
|
|
11797
|
+
function isTelemetryEvent(type) {
|
|
11798
|
+
if (!type)
|
|
11799
|
+
return false;
|
|
11800
|
+
return TELEMETRY_EVENT_TYPES.has(type);
|
|
11801
|
+
}
|
|
11802
|
+
var MAX_TITLE_CHARS = 40;
|
|
11803
|
+
function pad2(n) {
|
|
11804
|
+
return n < 10 ? `0${n}` : String(n);
|
|
11805
|
+
}
|
|
11806
|
+
function formatElapsed(ms) {
|
|
11807
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11808
|
+
return "0m";
|
|
11809
|
+
const totalMin = Math.floor(ms / 60000);
|
|
11810
|
+
if (totalMin < 60)
|
|
11811
|
+
return `${totalMin}m`;
|
|
11812
|
+
const totalHour = Math.floor(totalMin / 60);
|
|
11813
|
+
const min = totalMin % 60;
|
|
11814
|
+
if (totalHour < 24) {
|
|
11815
|
+
return min === 0 ? `${totalHour}h` : `${totalHour}h${min}m`;
|
|
11816
|
+
}
|
|
11817
|
+
const day = Math.floor(totalHour / 24);
|
|
11818
|
+
const hour = totalHour % 24;
|
|
11819
|
+
return hour === 0 ? `${day}d` : `${day}d${hour}h`;
|
|
11820
|
+
}
|
|
11821
|
+
function formatClock(ts) {
|
|
11822
|
+
const d = new Date(ts);
|
|
11823
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
11824
|
+
}
|
|
11825
|
+
function formatTimeoutLabel(ms) {
|
|
11826
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11827
|
+
return "0m";
|
|
11828
|
+
return formatElapsed(ms);
|
|
11829
|
+
}
|
|
11830
|
+
function truncateTitle(raw) {
|
|
11831
|
+
const s = raw.trim();
|
|
11832
|
+
if (s.length <= MAX_TITLE_CHARS)
|
|
11833
|
+
return s;
|
|
11834
|
+
return `${s.slice(0, MAX_TITLE_CHARS)}…`;
|
|
11835
|
+
}
|
|
11836
|
+
function pickString(v) {
|
|
11837
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11838
|
+
}
|
|
11839
|
+
function pickNumber(v) {
|
|
11840
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
11841
|
+
}
|
|
11842
|
+
function buildTelemetryNoticeText(type, payload, now = Date.now()) {
|
|
11843
|
+
const p = payload ?? {};
|
|
11844
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11845
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11846
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11847
|
+
let label;
|
|
11848
|
+
if (taskId !== undefined)
|
|
11849
|
+
label = `Task #${taskId}`;
|
|
11850
|
+
else if (bgId)
|
|
11851
|
+
label = bgId;
|
|
11852
|
+
else
|
|
11853
|
+
label = "后台任务";
|
|
11854
|
+
const head = type === "task.background.started" ? `\uD83D\uDD14 ${label}「${desc}」已启动` : `\uD83D\uDD04 ${label}「${desc}」后台运行中`;
|
|
11855
|
+
const parts = [];
|
|
11856
|
+
const startedAt = pickNumber(p.startedAt);
|
|
11857
|
+
const timeoutMs = pickNumber(p.timeoutMs);
|
|
11858
|
+
if (type !== "task.background.started" && startedAt !== undefined) {
|
|
11859
|
+
parts.push(`已跑 ${formatElapsed(now - startedAt)}`);
|
|
11860
|
+
}
|
|
11861
|
+
if (startedAt !== undefined && timeoutMs !== undefined) {
|
|
11862
|
+
parts.push(`预计 ${formatClock(startedAt + timeoutMs)} 超时(timeout ${formatTimeoutLabel(timeoutMs)})`);
|
|
11863
|
+
}
|
|
11864
|
+
let text = head;
|
|
11865
|
+
if (parts.length > 0)
|
|
11866
|
+
text += `
|
|
11867
|
+
${parts.join(",")}`;
|
|
11868
|
+
const progressMessage = pickString(p.progressMessage);
|
|
11869
|
+
if (progressMessage)
|
|
11870
|
+
text += `
|
|
11871
|
+
${progressMessage}`;
|
|
11872
|
+
return text;
|
|
11873
|
+
}
|
|
11874
|
+
var DEFAULT_SEND_TIMEOUT_MS = 5000;
|
|
11875
|
+
var MAX_IDEMPOTENCY_KEY_LEN = 50;
|
|
11876
|
+
function deriveTelemetryIdempotencyKey(eventId, chatId) {
|
|
11877
|
+
const anchor = pickString(eventId) ?? `ts-${Date.now()}-${Math.random()}`;
|
|
11878
|
+
return deriveIdempotencyKey(["telemetry", anchor, chatId]);
|
|
11879
|
+
}
|
|
11880
|
+
function buildLarkSendArgs(input) {
|
|
11881
|
+
const { chatId, text, profile, idempotencyKey } = input;
|
|
11882
|
+
const args = [
|
|
11883
|
+
"im",
|
|
11884
|
+
"+messages-send",
|
|
11885
|
+
"--chat-id",
|
|
11886
|
+
chatId,
|
|
11887
|
+
"--markdown",
|
|
11888
|
+
normalizeLarkAtMentions(text),
|
|
11889
|
+
"--as",
|
|
11890
|
+
"bot"
|
|
11891
|
+
];
|
|
11892
|
+
if (idempotencyKey) {
|
|
11893
|
+
args.push("--idempotency-key", idempotencyKey.slice(0, MAX_IDEMPOTENCY_KEY_LEN));
|
|
11894
|
+
}
|
|
11895
|
+
if (profile)
|
|
11896
|
+
args.unshift("--profile", profile);
|
|
11897
|
+
return args;
|
|
11898
|
+
}
|
|
11899
|
+
function sendLarkChatMarkdown(options) {
|
|
11900
|
+
const { chatId, text, profile, idempotencyKey } = options;
|
|
11901
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
11902
|
+
if (!chatId) {
|
|
11903
|
+
return Promise.reject(new Error("sendLarkChatMarkdown: empty chatId"));
|
|
11904
|
+
}
|
|
11905
|
+
return new Promise((resolve, reject) => {
|
|
11906
|
+
let settled = false;
|
|
11907
|
+
let timer;
|
|
11908
|
+
const finish = (err) => {
|
|
11909
|
+
if (settled)
|
|
11910
|
+
return;
|
|
11911
|
+
settled = true;
|
|
11912
|
+
if (timer)
|
|
11913
|
+
clearTimeout(timer);
|
|
11914
|
+
if (err)
|
|
11915
|
+
reject(err);
|
|
11916
|
+
else
|
|
11917
|
+
resolve();
|
|
11918
|
+
};
|
|
11919
|
+
const args = buildLarkSendArgs({ chatId, text, profile, idempotencyKey });
|
|
11920
|
+
let proc;
|
|
11921
|
+
try {
|
|
11922
|
+
proc = spawn("lark-cli", args, {
|
|
11923
|
+
shell: false,
|
|
11924
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11925
|
+
});
|
|
11926
|
+
} catch (err) {
|
|
11927
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
11928
|
+
return;
|
|
11929
|
+
}
|
|
11930
|
+
let stderr = "";
|
|
11931
|
+
proc.stderr?.on("data", (chunk) => {
|
|
11932
|
+
stderr += chunk.toString();
|
|
11933
|
+
});
|
|
11934
|
+
timer = setTimeout(() => {
|
|
11935
|
+
try {
|
|
11936
|
+
proc.kill("SIGKILL");
|
|
11937
|
+
} catch {}
|
|
11938
|
+
finish(new Error(`lark-cli timed out after ${timeoutMs}ms`));
|
|
11939
|
+
}, timeoutMs);
|
|
11940
|
+
timer.unref?.();
|
|
11941
|
+
proc.on("close", (code) => {
|
|
11942
|
+
if (code === 0)
|
|
11943
|
+
finish();
|
|
11944
|
+
else
|
|
11945
|
+
finish(new Error(`lark-cli exited with code ${code}: ${stderr}`));
|
|
11946
|
+
});
|
|
11947
|
+
proc.on("error", (err) => finish(err));
|
|
11948
|
+
});
|
|
11949
|
+
}
|
|
11950
|
+
function targetFromEventMetadata(metadata) {
|
|
11951
|
+
if (!metadata)
|
|
11952
|
+
return;
|
|
11953
|
+
const replyChannel = metadata.replyChannel;
|
|
11954
|
+
const chatId = pickString(replyChannel?.chatId) ?? pickString(metadata.chatId);
|
|
11955
|
+
if (!chatId)
|
|
11956
|
+
return;
|
|
11957
|
+
const profile = pickString(replyChannel?.profile) ?? pickString(metadata.profile);
|
|
11958
|
+
return profile ? { chatId, profile } : { chatId };
|
|
11959
|
+
}
|
|
11960
|
+
async function targetFromTriggerEvent(eventStore, triggerEventId) {
|
|
11961
|
+
if (!eventStore || !triggerEventId)
|
|
11962
|
+
return;
|
|
11963
|
+
let persisted;
|
|
11964
|
+
try {
|
|
11965
|
+
persisted = await eventStore.getEventById(triggerEventId);
|
|
11966
|
+
} catch {
|
|
11967
|
+
return;
|
|
11968
|
+
}
|
|
11969
|
+
if (!persisted || typeof persisted !== "object")
|
|
11970
|
+
return;
|
|
11971
|
+
const metadata = persisted.metadata;
|
|
11972
|
+
if (!metadata || typeof metadata !== "object")
|
|
11973
|
+
return;
|
|
11974
|
+
return targetFromEventMetadata(metadata);
|
|
11975
|
+
}
|
|
11976
|
+
|
|
11977
|
+
// src/commands/shared/self-echo-filter.ts
|
|
11978
|
+
var BOT_SENDER_TYPES = new Set(["bot", "app"]);
|
|
11979
|
+
function pickString2(v) {
|
|
11980
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11981
|
+
}
|
|
11982
|
+
function extractSenderIdentity(event) {
|
|
11983
|
+
if (!event)
|
|
11984
|
+
return {};
|
|
11985
|
+
const metadata = event.metadata;
|
|
11986
|
+
const payload = event.payload;
|
|
11987
|
+
const payloadMeta = payload?.metadata;
|
|
11988
|
+
const rawEvent = payload?.rawEvent;
|
|
11989
|
+
const innerEvent = rawEvent?.event;
|
|
11990
|
+
const innerSender = innerEvent?.sender;
|
|
11991
|
+
const topSender = rawEvent?.sender;
|
|
11992
|
+
const senderType = pickString2(metadata?.senderType) ?? pickString2(payloadMeta?.senderType) ?? pickString2(rawEvent?.sender_type) ?? pickString2(innerSender?.sender_type) ?? pickString2(topSender?.sender_type) ?? pickString2(payload?.senderType);
|
|
11993
|
+
const nestedSenderId = innerSender?.sender_id ?? topSender?.sender_id;
|
|
11994
|
+
const senderId = pickString2(metadata?.senderId) ?? pickString2(payloadMeta?.senderId) ?? pickString2(payload?.senderId) ?? pickString2(rawEvent?.sender_id) ?? (typeof nestedSenderId === "string" ? pickString2(nestedSenderId) : pickString2(nestedSenderId?.open_id) ?? pickString2(nestedSenderId?.user_id) ?? pickString2(nestedSenderId?.union_id));
|
|
11995
|
+
return {
|
|
11996
|
+
...senderType ? { senderType } : {},
|
|
11997
|
+
...senderId ? { senderId } : {}
|
|
11998
|
+
};
|
|
11999
|
+
}
|
|
12000
|
+
function isSelfEchoEvent(event, options = {}) {
|
|
12001
|
+
const { senderType, senderId } = extractSenderIdentity(event);
|
|
12002
|
+
if (options.enabled === false) {
|
|
12003
|
+
return {
|
|
12004
|
+
skip: false,
|
|
12005
|
+
reason: "disabled",
|
|
12006
|
+
...senderType ? { senderType } : {},
|
|
12007
|
+
...senderId ? { senderId } : {}
|
|
12008
|
+
};
|
|
12009
|
+
}
|
|
12010
|
+
if (senderType && BOT_SENDER_TYPES.has(senderType.toLowerCase())) {
|
|
12011
|
+
return {
|
|
12012
|
+
skip: true,
|
|
12013
|
+
reason: "bot-sender",
|
|
12014
|
+
...senderType ? { senderType } : {},
|
|
12015
|
+
...senderId ? { senderId } : {}
|
|
12016
|
+
};
|
|
12017
|
+
}
|
|
12018
|
+
if (senderId && options.selfBotIds && options.selfBotIds.length > 0) {
|
|
12019
|
+
if (options.selfBotIds.includes(senderId)) {
|
|
12020
|
+
return {
|
|
12021
|
+
skip: true,
|
|
12022
|
+
reason: "self-bot-id",
|
|
12023
|
+
...senderType ? { senderType } : {},
|
|
12024
|
+
senderId
|
|
12025
|
+
};
|
|
12026
|
+
}
|
|
12027
|
+
}
|
|
12028
|
+
return {
|
|
12029
|
+
skip: false,
|
|
12030
|
+
...senderType ? { senderType } : {},
|
|
12031
|
+
...senderId ? { senderId } : {}
|
|
12032
|
+
};
|
|
12033
|
+
}
|
|
12034
|
+
var SELF_ECHO_FILTER_ENABLED_ENV = "ROY_AGENT_SELF_ECHO_FILTER";
|
|
12035
|
+
function readSelfEchoFilterEnabled(env = process.env) {
|
|
12036
|
+
const raw = env[SELF_ECHO_FILTER_ENABLED_ENV];
|
|
12037
|
+
if (raw === undefined)
|
|
12038
|
+
return true;
|
|
12039
|
+
const v = raw.trim().toLowerCase();
|
|
12040
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
12041
|
+
}
|
|
12042
|
+
|
|
12043
|
+
// src/commands/shared/event-handler.ts
|
|
11788
12044
|
function parseLarkPerTaskChatId(contextJson) {
|
|
11789
12045
|
if (!contextJson)
|
|
11790
12046
|
return;
|
|
@@ -11837,6 +12093,15 @@ class EventHandler {
|
|
|
11837
12093
|
resolvedSessionByTrigger = new Map;
|
|
11838
12094
|
eventStore = null;
|
|
11839
12095
|
getHandlerForEsType = null;
|
|
12096
|
+
telemetrySender = (notice) => sendLarkChatMarkdown(notice);
|
|
12097
|
+
selfEchoFilter = {
|
|
12098
|
+
enabled: true,
|
|
12099
|
+
selfBotIds: []
|
|
12100
|
+
};
|
|
12101
|
+
selfEchoDroppedCount = 0;
|
|
12102
|
+
getSelfEchoDroppedCount() {
|
|
12103
|
+
return this.selfEchoDroppedCount;
|
|
12104
|
+
}
|
|
11840
12105
|
constructor(options) {
|
|
11841
12106
|
this.env = options.env;
|
|
11842
12107
|
this.eventTypes = options.eventTypes;
|
|
@@ -11851,6 +12116,11 @@ class EventHandler {
|
|
|
11851
12116
|
this.sessionComponent = options.sessionComponent ?? null;
|
|
11852
12117
|
this.eventStore = options.eventStore ?? null;
|
|
11853
12118
|
this.getHandlerForEsType = options.getHandlerForEsType ?? null;
|
|
12119
|
+
this.telemetrySender = options.telemetrySender ?? ((notice) => sendLarkChatMarkdown(notice));
|
|
12120
|
+
this.selfEchoFilter = {
|
|
12121
|
+
enabled: options.selfEchoFilter?.enabled ?? readSelfEchoFilterEnabled(),
|
|
12122
|
+
selfBotIds: options.selfEchoFilter?.selfBotIds ?? []
|
|
12123
|
+
};
|
|
11854
12124
|
}
|
|
11855
12125
|
start() {
|
|
11856
12126
|
this.isStopped = false;
|
|
@@ -11897,9 +12167,45 @@ class EventHandler {
|
|
|
11897
12167
|
if (this.isStopped) {
|
|
11898
12168
|
return;
|
|
11899
12169
|
}
|
|
12170
|
+
if (this.selfEchoFilter.enabled) {
|
|
12171
|
+
const verdict = isSelfEchoEvent(event, {
|
|
12172
|
+
selfBotIds: this.selfEchoFilter.selfBotIds
|
|
12173
|
+
});
|
|
12174
|
+
if (verdict.skip) {
|
|
12175
|
+
this.selfEchoDroppedCount += 1;
|
|
12176
|
+
console.error(`[EventHandler] self-echo event dropped (reason=${verdict.reason}` + `, senderType=${verdict.senderType ?? "-"}` + `, senderId=${verdict.senderId ?? "-"}` + `, type=${event.type})`);
|
|
12177
|
+
return;
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
if (isTelemetryEvent(event.type)) {
|
|
12181
|
+
this.sendTelemetryNotice(event);
|
|
12182
|
+
return;
|
|
12183
|
+
}
|
|
11900
12184
|
const formatted = await this.formatEvent(event);
|
|
11901
12185
|
this.enqueue(formatted);
|
|
11902
12186
|
}
|
|
12187
|
+
async sendTelemetryNotice(event) {
|
|
12188
|
+
try {
|
|
12189
|
+
const metadata = event.metadata;
|
|
12190
|
+
let target = targetFromEventMetadata(metadata);
|
|
12191
|
+
if (!target) {
|
|
12192
|
+
const triggerEventId = typeof metadata?.triggerEventId === "string" ? metadata.triggerEventId : undefined;
|
|
12193
|
+
target = await targetFromTriggerEvent(this.eventStore, triggerEventId);
|
|
12194
|
+
}
|
|
12195
|
+
if (!target) {
|
|
12196
|
+
return;
|
|
12197
|
+
}
|
|
12198
|
+
const text = buildTelemetryNoticeText(event.type, event.payload);
|
|
12199
|
+
await this.telemetrySender({
|
|
12200
|
+
chatId: target.chatId,
|
|
12201
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12202
|
+
text,
|
|
12203
|
+
idempotencyKey: deriveTelemetryIdempotencyKey(event.id, target.chatId)
|
|
12204
|
+
});
|
|
12205
|
+
} catch (err) {
|
|
12206
|
+
console.error(`[EventHandler] telemetry notice failed (swallowed) type=${event.type}`, err);
|
|
12207
|
+
}
|
|
12208
|
+
}
|
|
11903
12209
|
async formatEvent(event) {
|
|
11904
12210
|
if (NOTIFY_V2_TERMINAL_TYPES.has(event.type) && event.metadata?.triggerEventId && this.eventStore && this.getHandlerForEsType) {
|
|
11905
12211
|
try {
|
|
@@ -12387,6 +12693,14 @@ __legacyDecorateClassTS([
|
|
|
12387
12693
|
]),
|
|
12388
12694
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12389
12695
|
], EventHandler.prototype, "handleEvent", null);
|
|
12696
|
+
__legacyDecorateClassTS([
|
|
12697
|
+
TracedAs5("event_handler.telemetry_notice", { recordParams: true, recordResult: false }),
|
|
12698
|
+
__legacyMetadataTS("design:type", Function),
|
|
12699
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12700
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12701
|
+
]),
|
|
12702
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12703
|
+
], EventHandler.prototype, "sendTelemetryNotice", null);
|
|
12390
12704
|
__legacyDecorateClassTS([
|
|
12391
12705
|
TracedAs5("cli.event-handler.formatEvent", { recordParams: true, recordResult: false }),
|
|
12392
12706
|
__legacyMetadataTS("design:type", Function),
|
package/dist/index.js
CHANGED
|
@@ -9638,7 +9638,7 @@ class CliQuietModeService {
|
|
|
9638
9638
|
// package.json
|
|
9639
9639
|
var package_default = {
|
|
9640
9640
|
name: "@ai-setting/roy-agent-cli",
|
|
9641
|
-
version: "1.7.
|
|
9641
|
+
version: "1.7.58",
|
|
9642
9642
|
type: "module",
|
|
9643
9643
|
description: "CLI for roy-agent - Non-interactive command execution",
|
|
9644
9644
|
main: "./dist/index.js",
|
|
@@ -11784,6 +11784,262 @@ import {
|
|
|
11784
11784
|
} from "@ai-setting/roy-agent-core/env";
|
|
11785
11785
|
import { TracedAs as TracedAs5 } from "@ai-setting/roy-agent-core";
|
|
11786
11786
|
import { notifyReactFailure as notifyReactFailure2 } from "@ai-setting/roy-agent-core/env/error-reply";
|
|
11787
|
+
|
|
11788
|
+
// src/commands/shared/telemetry-notice.ts
|
|
11789
|
+
import { spawn } from "node:child_process";
|
|
11790
|
+
import { normalizeLarkAtMentions } from "@ai-setting/roy-agent-core/env/lark-mention-utils";
|
|
11791
|
+
import { deriveIdempotencyKey } from "@ai-setting/roy-agent-core/env/event-source/idempotency-key";
|
|
11792
|
+
var TELEMETRY_EVENT_TYPES = new Set([
|
|
11793
|
+
"task.background.started",
|
|
11794
|
+
"task.background.progress"
|
|
11795
|
+
]);
|
|
11796
|
+
function isTelemetryEvent(type) {
|
|
11797
|
+
if (!type)
|
|
11798
|
+
return false;
|
|
11799
|
+
return TELEMETRY_EVENT_TYPES.has(type);
|
|
11800
|
+
}
|
|
11801
|
+
var MAX_TITLE_CHARS = 40;
|
|
11802
|
+
function pad2(n) {
|
|
11803
|
+
return n < 10 ? `0${n}` : String(n);
|
|
11804
|
+
}
|
|
11805
|
+
function formatElapsed(ms) {
|
|
11806
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11807
|
+
return "0m";
|
|
11808
|
+
const totalMin = Math.floor(ms / 60000);
|
|
11809
|
+
if (totalMin < 60)
|
|
11810
|
+
return `${totalMin}m`;
|
|
11811
|
+
const totalHour = Math.floor(totalMin / 60);
|
|
11812
|
+
const min = totalMin % 60;
|
|
11813
|
+
if (totalHour < 24) {
|
|
11814
|
+
return min === 0 ? `${totalHour}h` : `${totalHour}h${min}m`;
|
|
11815
|
+
}
|
|
11816
|
+
const day = Math.floor(totalHour / 24);
|
|
11817
|
+
const hour = totalHour % 24;
|
|
11818
|
+
return hour === 0 ? `${day}d` : `${day}d${hour}h`;
|
|
11819
|
+
}
|
|
11820
|
+
function formatClock(ts) {
|
|
11821
|
+
const d = new Date(ts);
|
|
11822
|
+
return `${pad2(d.getHours())}:${pad2(d.getMinutes())}`;
|
|
11823
|
+
}
|
|
11824
|
+
function formatTimeoutLabel(ms) {
|
|
11825
|
+
if (!Number.isFinite(ms) || ms <= 0)
|
|
11826
|
+
return "0m";
|
|
11827
|
+
return formatElapsed(ms);
|
|
11828
|
+
}
|
|
11829
|
+
function truncateTitle(raw) {
|
|
11830
|
+
const s = raw.trim();
|
|
11831
|
+
if (s.length <= MAX_TITLE_CHARS)
|
|
11832
|
+
return s;
|
|
11833
|
+
return `${s.slice(0, MAX_TITLE_CHARS)}…`;
|
|
11834
|
+
}
|
|
11835
|
+
function pickString(v) {
|
|
11836
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11837
|
+
}
|
|
11838
|
+
function pickNumber(v) {
|
|
11839
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
11840
|
+
}
|
|
11841
|
+
function buildTelemetryNoticeText(type, payload, now = Date.now()) {
|
|
11842
|
+
const p = payload ?? {};
|
|
11843
|
+
const taskId = pickNumber(p.associatedTaskId);
|
|
11844
|
+
const bgId = pickString(p.backgroundTaskId);
|
|
11845
|
+
const desc = truncateTitle(pickString(p.description) ?? "后台任务");
|
|
11846
|
+
let label;
|
|
11847
|
+
if (taskId !== undefined)
|
|
11848
|
+
label = `Task #${taskId}`;
|
|
11849
|
+
else if (bgId)
|
|
11850
|
+
label = bgId;
|
|
11851
|
+
else
|
|
11852
|
+
label = "后台任务";
|
|
11853
|
+
const head = type === "task.background.started" ? `\uD83D\uDD14 ${label}「${desc}」已启动` : `\uD83D\uDD04 ${label}「${desc}」后台运行中`;
|
|
11854
|
+
const parts = [];
|
|
11855
|
+
const startedAt = pickNumber(p.startedAt);
|
|
11856
|
+
const timeoutMs = pickNumber(p.timeoutMs);
|
|
11857
|
+
if (type !== "task.background.started" && startedAt !== undefined) {
|
|
11858
|
+
parts.push(`已跑 ${formatElapsed(now - startedAt)}`);
|
|
11859
|
+
}
|
|
11860
|
+
if (startedAt !== undefined && timeoutMs !== undefined) {
|
|
11861
|
+
parts.push(`预计 ${formatClock(startedAt + timeoutMs)} 超时(timeout ${formatTimeoutLabel(timeoutMs)})`);
|
|
11862
|
+
}
|
|
11863
|
+
let text = head;
|
|
11864
|
+
if (parts.length > 0)
|
|
11865
|
+
text += `
|
|
11866
|
+
${parts.join(",")}`;
|
|
11867
|
+
const progressMessage = pickString(p.progressMessage);
|
|
11868
|
+
if (progressMessage)
|
|
11869
|
+
text += `
|
|
11870
|
+
${progressMessage}`;
|
|
11871
|
+
return text;
|
|
11872
|
+
}
|
|
11873
|
+
var DEFAULT_SEND_TIMEOUT_MS = 5000;
|
|
11874
|
+
var MAX_IDEMPOTENCY_KEY_LEN = 50;
|
|
11875
|
+
function deriveTelemetryIdempotencyKey(eventId, chatId) {
|
|
11876
|
+
const anchor = pickString(eventId) ?? `ts-${Date.now()}-${Math.random()}`;
|
|
11877
|
+
return deriveIdempotencyKey(["telemetry", anchor, chatId]);
|
|
11878
|
+
}
|
|
11879
|
+
function buildLarkSendArgs(input) {
|
|
11880
|
+
const { chatId, text, profile, idempotencyKey } = input;
|
|
11881
|
+
const args = [
|
|
11882
|
+
"im",
|
|
11883
|
+
"+messages-send",
|
|
11884
|
+
"--chat-id",
|
|
11885
|
+
chatId,
|
|
11886
|
+
"--markdown",
|
|
11887
|
+
normalizeLarkAtMentions(text),
|
|
11888
|
+
"--as",
|
|
11889
|
+
"bot"
|
|
11890
|
+
];
|
|
11891
|
+
if (idempotencyKey) {
|
|
11892
|
+
args.push("--idempotency-key", idempotencyKey.slice(0, MAX_IDEMPOTENCY_KEY_LEN));
|
|
11893
|
+
}
|
|
11894
|
+
if (profile)
|
|
11895
|
+
args.unshift("--profile", profile);
|
|
11896
|
+
return args;
|
|
11897
|
+
}
|
|
11898
|
+
function sendLarkChatMarkdown(options) {
|
|
11899
|
+
const { chatId, text, profile, idempotencyKey } = options;
|
|
11900
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
11901
|
+
if (!chatId) {
|
|
11902
|
+
return Promise.reject(new Error("sendLarkChatMarkdown: empty chatId"));
|
|
11903
|
+
}
|
|
11904
|
+
return new Promise((resolve, reject) => {
|
|
11905
|
+
let settled = false;
|
|
11906
|
+
let timer;
|
|
11907
|
+
const finish = (err) => {
|
|
11908
|
+
if (settled)
|
|
11909
|
+
return;
|
|
11910
|
+
settled = true;
|
|
11911
|
+
if (timer)
|
|
11912
|
+
clearTimeout(timer);
|
|
11913
|
+
if (err)
|
|
11914
|
+
reject(err);
|
|
11915
|
+
else
|
|
11916
|
+
resolve();
|
|
11917
|
+
};
|
|
11918
|
+
const args = buildLarkSendArgs({ chatId, text, profile, idempotencyKey });
|
|
11919
|
+
let proc;
|
|
11920
|
+
try {
|
|
11921
|
+
proc = spawn("lark-cli", args, {
|
|
11922
|
+
shell: false,
|
|
11923
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
11924
|
+
});
|
|
11925
|
+
} catch (err) {
|
|
11926
|
+
finish(err instanceof Error ? err : new Error(String(err)));
|
|
11927
|
+
return;
|
|
11928
|
+
}
|
|
11929
|
+
let stderr = "";
|
|
11930
|
+
proc.stderr?.on("data", (chunk) => {
|
|
11931
|
+
stderr += chunk.toString();
|
|
11932
|
+
});
|
|
11933
|
+
timer = setTimeout(() => {
|
|
11934
|
+
try {
|
|
11935
|
+
proc.kill("SIGKILL");
|
|
11936
|
+
} catch {}
|
|
11937
|
+
finish(new Error(`lark-cli timed out after ${timeoutMs}ms`));
|
|
11938
|
+
}, timeoutMs);
|
|
11939
|
+
timer.unref?.();
|
|
11940
|
+
proc.on("close", (code) => {
|
|
11941
|
+
if (code === 0)
|
|
11942
|
+
finish();
|
|
11943
|
+
else
|
|
11944
|
+
finish(new Error(`lark-cli exited with code ${code}: ${stderr}`));
|
|
11945
|
+
});
|
|
11946
|
+
proc.on("error", (err) => finish(err));
|
|
11947
|
+
});
|
|
11948
|
+
}
|
|
11949
|
+
function targetFromEventMetadata(metadata) {
|
|
11950
|
+
if (!metadata)
|
|
11951
|
+
return;
|
|
11952
|
+
const replyChannel = metadata.replyChannel;
|
|
11953
|
+
const chatId = pickString(replyChannel?.chatId) ?? pickString(metadata.chatId);
|
|
11954
|
+
if (!chatId)
|
|
11955
|
+
return;
|
|
11956
|
+
const profile = pickString(replyChannel?.profile) ?? pickString(metadata.profile);
|
|
11957
|
+
return profile ? { chatId, profile } : { chatId };
|
|
11958
|
+
}
|
|
11959
|
+
async function targetFromTriggerEvent(eventStore, triggerEventId) {
|
|
11960
|
+
if (!eventStore || !triggerEventId)
|
|
11961
|
+
return;
|
|
11962
|
+
let persisted;
|
|
11963
|
+
try {
|
|
11964
|
+
persisted = await eventStore.getEventById(triggerEventId);
|
|
11965
|
+
} catch {
|
|
11966
|
+
return;
|
|
11967
|
+
}
|
|
11968
|
+
if (!persisted || typeof persisted !== "object")
|
|
11969
|
+
return;
|
|
11970
|
+
const metadata = persisted.metadata;
|
|
11971
|
+
if (!metadata || typeof metadata !== "object")
|
|
11972
|
+
return;
|
|
11973
|
+
return targetFromEventMetadata(metadata);
|
|
11974
|
+
}
|
|
11975
|
+
|
|
11976
|
+
// src/commands/shared/self-echo-filter.ts
|
|
11977
|
+
var BOT_SENDER_TYPES = new Set(["bot", "app"]);
|
|
11978
|
+
function pickString2(v) {
|
|
11979
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
11980
|
+
}
|
|
11981
|
+
function extractSenderIdentity(event) {
|
|
11982
|
+
if (!event)
|
|
11983
|
+
return {};
|
|
11984
|
+
const metadata = event.metadata;
|
|
11985
|
+
const payload = event.payload;
|
|
11986
|
+
const payloadMeta = payload?.metadata;
|
|
11987
|
+
const rawEvent = payload?.rawEvent;
|
|
11988
|
+
const innerEvent = rawEvent?.event;
|
|
11989
|
+
const innerSender = innerEvent?.sender;
|
|
11990
|
+
const topSender = rawEvent?.sender;
|
|
11991
|
+
const senderType = pickString2(metadata?.senderType) ?? pickString2(payloadMeta?.senderType) ?? pickString2(rawEvent?.sender_type) ?? pickString2(innerSender?.sender_type) ?? pickString2(topSender?.sender_type) ?? pickString2(payload?.senderType);
|
|
11992
|
+
const nestedSenderId = innerSender?.sender_id ?? topSender?.sender_id;
|
|
11993
|
+
const senderId = pickString2(metadata?.senderId) ?? pickString2(payloadMeta?.senderId) ?? pickString2(payload?.senderId) ?? pickString2(rawEvent?.sender_id) ?? (typeof nestedSenderId === "string" ? pickString2(nestedSenderId) : pickString2(nestedSenderId?.open_id) ?? pickString2(nestedSenderId?.user_id) ?? pickString2(nestedSenderId?.union_id));
|
|
11994
|
+
return {
|
|
11995
|
+
...senderType ? { senderType } : {},
|
|
11996
|
+
...senderId ? { senderId } : {}
|
|
11997
|
+
};
|
|
11998
|
+
}
|
|
11999
|
+
function isSelfEchoEvent(event, options = {}) {
|
|
12000
|
+
const { senderType, senderId } = extractSenderIdentity(event);
|
|
12001
|
+
if (options.enabled === false) {
|
|
12002
|
+
return {
|
|
12003
|
+
skip: false,
|
|
12004
|
+
reason: "disabled",
|
|
12005
|
+
...senderType ? { senderType } : {},
|
|
12006
|
+
...senderId ? { senderId } : {}
|
|
12007
|
+
};
|
|
12008
|
+
}
|
|
12009
|
+
if (senderType && BOT_SENDER_TYPES.has(senderType.toLowerCase())) {
|
|
12010
|
+
return {
|
|
12011
|
+
skip: true,
|
|
12012
|
+
reason: "bot-sender",
|
|
12013
|
+
...senderType ? { senderType } : {},
|
|
12014
|
+
...senderId ? { senderId } : {}
|
|
12015
|
+
};
|
|
12016
|
+
}
|
|
12017
|
+
if (senderId && options.selfBotIds && options.selfBotIds.length > 0) {
|
|
12018
|
+
if (options.selfBotIds.includes(senderId)) {
|
|
12019
|
+
return {
|
|
12020
|
+
skip: true,
|
|
12021
|
+
reason: "self-bot-id",
|
|
12022
|
+
...senderType ? { senderType } : {},
|
|
12023
|
+
senderId
|
|
12024
|
+
};
|
|
12025
|
+
}
|
|
12026
|
+
}
|
|
12027
|
+
return {
|
|
12028
|
+
skip: false,
|
|
12029
|
+
...senderType ? { senderType } : {},
|
|
12030
|
+
...senderId ? { senderId } : {}
|
|
12031
|
+
};
|
|
12032
|
+
}
|
|
12033
|
+
var SELF_ECHO_FILTER_ENABLED_ENV = "ROY_AGENT_SELF_ECHO_FILTER";
|
|
12034
|
+
function readSelfEchoFilterEnabled(env = process.env) {
|
|
12035
|
+
const raw = env[SELF_ECHO_FILTER_ENABLED_ENV];
|
|
12036
|
+
if (raw === undefined)
|
|
12037
|
+
return true;
|
|
12038
|
+
const v = raw.trim().toLowerCase();
|
|
12039
|
+
return !(v === "0" || v === "false" || v === "off" || v === "no");
|
|
12040
|
+
}
|
|
12041
|
+
|
|
12042
|
+
// src/commands/shared/event-handler.ts
|
|
11787
12043
|
function parseLarkPerTaskChatId(contextJson) {
|
|
11788
12044
|
if (!contextJson)
|
|
11789
12045
|
return;
|
|
@@ -11836,6 +12092,15 @@ class EventHandler {
|
|
|
11836
12092
|
resolvedSessionByTrigger = new Map;
|
|
11837
12093
|
eventStore = null;
|
|
11838
12094
|
getHandlerForEsType = null;
|
|
12095
|
+
telemetrySender = (notice) => sendLarkChatMarkdown(notice);
|
|
12096
|
+
selfEchoFilter = {
|
|
12097
|
+
enabled: true,
|
|
12098
|
+
selfBotIds: []
|
|
12099
|
+
};
|
|
12100
|
+
selfEchoDroppedCount = 0;
|
|
12101
|
+
getSelfEchoDroppedCount() {
|
|
12102
|
+
return this.selfEchoDroppedCount;
|
|
12103
|
+
}
|
|
11839
12104
|
constructor(options) {
|
|
11840
12105
|
this.env = options.env;
|
|
11841
12106
|
this.eventTypes = options.eventTypes;
|
|
@@ -11850,6 +12115,11 @@ class EventHandler {
|
|
|
11850
12115
|
this.sessionComponent = options.sessionComponent ?? null;
|
|
11851
12116
|
this.eventStore = options.eventStore ?? null;
|
|
11852
12117
|
this.getHandlerForEsType = options.getHandlerForEsType ?? null;
|
|
12118
|
+
this.telemetrySender = options.telemetrySender ?? ((notice) => sendLarkChatMarkdown(notice));
|
|
12119
|
+
this.selfEchoFilter = {
|
|
12120
|
+
enabled: options.selfEchoFilter?.enabled ?? readSelfEchoFilterEnabled(),
|
|
12121
|
+
selfBotIds: options.selfEchoFilter?.selfBotIds ?? []
|
|
12122
|
+
};
|
|
11853
12123
|
}
|
|
11854
12124
|
start() {
|
|
11855
12125
|
this.isStopped = false;
|
|
@@ -11896,9 +12166,45 @@ class EventHandler {
|
|
|
11896
12166
|
if (this.isStopped) {
|
|
11897
12167
|
return;
|
|
11898
12168
|
}
|
|
12169
|
+
if (this.selfEchoFilter.enabled) {
|
|
12170
|
+
const verdict = isSelfEchoEvent(event, {
|
|
12171
|
+
selfBotIds: this.selfEchoFilter.selfBotIds
|
|
12172
|
+
});
|
|
12173
|
+
if (verdict.skip) {
|
|
12174
|
+
this.selfEchoDroppedCount += 1;
|
|
12175
|
+
console.error(`[EventHandler] self-echo event dropped (reason=${verdict.reason}` + `, senderType=${verdict.senderType ?? "-"}` + `, senderId=${verdict.senderId ?? "-"}` + `, type=${event.type})`);
|
|
12176
|
+
return;
|
|
12177
|
+
}
|
|
12178
|
+
}
|
|
12179
|
+
if (isTelemetryEvent(event.type)) {
|
|
12180
|
+
this.sendTelemetryNotice(event);
|
|
12181
|
+
return;
|
|
12182
|
+
}
|
|
11899
12183
|
const formatted = await this.formatEvent(event);
|
|
11900
12184
|
this.enqueue(formatted);
|
|
11901
12185
|
}
|
|
12186
|
+
async sendTelemetryNotice(event) {
|
|
12187
|
+
try {
|
|
12188
|
+
const metadata = event.metadata;
|
|
12189
|
+
let target = targetFromEventMetadata(metadata);
|
|
12190
|
+
if (!target) {
|
|
12191
|
+
const triggerEventId = typeof metadata?.triggerEventId === "string" ? metadata.triggerEventId : undefined;
|
|
12192
|
+
target = await targetFromTriggerEvent(this.eventStore, triggerEventId);
|
|
12193
|
+
}
|
|
12194
|
+
if (!target) {
|
|
12195
|
+
return;
|
|
12196
|
+
}
|
|
12197
|
+
const text = buildTelemetryNoticeText(event.type, event.payload);
|
|
12198
|
+
await this.telemetrySender({
|
|
12199
|
+
chatId: target.chatId,
|
|
12200
|
+
...target.profile ? { profile: target.profile } : {},
|
|
12201
|
+
text,
|
|
12202
|
+
idempotencyKey: deriveTelemetryIdempotencyKey(event.id, target.chatId)
|
|
12203
|
+
});
|
|
12204
|
+
} catch (err) {
|
|
12205
|
+
console.error(`[EventHandler] telemetry notice failed (swallowed) type=${event.type}`, err);
|
|
12206
|
+
}
|
|
12207
|
+
}
|
|
11902
12208
|
async formatEvent(event) {
|
|
11903
12209
|
if (NOTIFY_V2_TERMINAL_TYPES.has(event.type) && event.metadata?.triggerEventId && this.eventStore && this.getHandlerForEsType) {
|
|
11904
12210
|
try {
|
|
@@ -12386,6 +12692,14 @@ __legacyDecorateClassTS([
|
|
|
12386
12692
|
]),
|
|
12387
12693
|
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12388
12694
|
], EventHandler.prototype, "handleEvent", null);
|
|
12695
|
+
__legacyDecorateClassTS([
|
|
12696
|
+
TracedAs5("event_handler.telemetry_notice", { recordParams: true, recordResult: false }),
|
|
12697
|
+
__legacyMetadataTS("design:type", Function),
|
|
12698
|
+
__legacyMetadataTS("design:paramtypes", [
|
|
12699
|
+
typeof EnvEvent === "undefined" ? Object : EnvEvent
|
|
12700
|
+
]),
|
|
12701
|
+
__legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
|
|
12702
|
+
], EventHandler.prototype, "sendTelemetryNotice", null);
|
|
12389
12703
|
__legacyDecorateClassTS([
|
|
12390
12704
|
TracedAs5("cli.event-handler.formatEvent", { recordParams: true, recordResult: false }),
|
|
12391
12705
|
__legacyMetadataTS("design:type", Function),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/dist/roy-agent-linux-x64/bin/roy-agent
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 109897856 bytes
|
|
4
|
+
sha256: 0343f071cd373c6540cd1f19d6a561e9cacce93efac9be7b3f424490521d7044
|