@yoooclaw/phone-notifications 1.10.6 → 1.11.0-beta.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.cjs +386 -129
- package/dist/index.cjs.map +1 -1
- package/package.json +1 -1
- package/skills/notification-monitor/SKILL.md +83 -81
package/dist/index.cjs
CHANGED
|
@@ -52,24 +52,24 @@ function expandUserPath(value) {
|
|
|
52
52
|
return homeDir();
|
|
53
53
|
}
|
|
54
54
|
if (value.startsWith("~/")) {
|
|
55
|
-
return (0,
|
|
55
|
+
return (0, import_node_path5.join)(homeDir(), value.slice(2));
|
|
56
56
|
}
|
|
57
57
|
return value;
|
|
58
58
|
}
|
|
59
59
|
function candidateMetaPaths() {
|
|
60
60
|
const home = homeDir();
|
|
61
61
|
return [
|
|
62
|
-
(0,
|
|
63
|
-
(0,
|
|
62
|
+
(0, import_node_path5.join)(home, ".qclaw", "qclaw.json"),
|
|
63
|
+
(0, import_node_path5.join)(home, ".qclow", "qclaw.json")
|
|
64
64
|
];
|
|
65
65
|
}
|
|
66
66
|
function loadQClawMeta() {
|
|
67
67
|
for (const metaPath of candidateMetaPaths()) {
|
|
68
|
-
if (!(0,
|
|
68
|
+
if (!(0, import_node_fs6.existsSync)(metaPath)) {
|
|
69
69
|
continue;
|
|
70
70
|
}
|
|
71
71
|
try {
|
|
72
|
-
const parsed = JSON.parse((0,
|
|
72
|
+
const parsed = JSON.parse((0, import_node_fs6.readFileSync)(metaPath, "utf-8"));
|
|
73
73
|
return {
|
|
74
74
|
stateDir: expandUserPath(trimToUndefined(parsed?.stateDir)),
|
|
75
75
|
configPath: expandUserPath(trimToUndefined(parsed?.configPath))
|
|
@@ -90,12 +90,12 @@ function resolveConfigPathFromEnv() {
|
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
function hasOpenClawMarkers() {
|
|
93
|
-
const baseDir = (0,
|
|
93
|
+
const baseDir = (0, import_node_path5.join)(homeDir(), ".openclaw");
|
|
94
94
|
return [
|
|
95
|
-
(0,
|
|
96
|
-
(0,
|
|
97
|
-
(0,
|
|
98
|
-
].some((candidate) => (0,
|
|
95
|
+
(0, import_node_path5.join)(baseDir, "openclaw.json"),
|
|
96
|
+
(0, import_node_path5.join)(baseDir, "credentials.json"),
|
|
97
|
+
(0, import_node_path5.join)(baseDir, "extensions")
|
|
98
|
+
].some((candidate) => (0, import_node_fs6.existsSync)(candidate));
|
|
99
99
|
}
|
|
100
100
|
function resolveStateDir() {
|
|
101
101
|
const envDir = resolveStateDirFromEnv();
|
|
@@ -103,16 +103,16 @@ function resolveStateDir() {
|
|
|
103
103
|
return envDir;
|
|
104
104
|
}
|
|
105
105
|
if (hasOpenClawMarkers()) {
|
|
106
|
-
return (0,
|
|
106
|
+
return (0, import_node_path5.join)(homeDir(), ".openclaw");
|
|
107
107
|
}
|
|
108
108
|
const meta = loadQClawMeta();
|
|
109
109
|
if (meta?.stateDir) {
|
|
110
110
|
return meta.stateDir;
|
|
111
111
|
}
|
|
112
112
|
if (meta?.configPath) {
|
|
113
|
-
return (0,
|
|
113
|
+
return (0, import_node_path5.dirname)(meta.configPath);
|
|
114
114
|
}
|
|
115
|
-
return (0,
|
|
115
|
+
return (0, import_node_path5.join)(homeDir(), ".openclaw");
|
|
116
116
|
}
|
|
117
117
|
function resolveConfigPath(stateDir = resolveStateDir()) {
|
|
118
118
|
const envConfigPath = resolveConfigPathFromEnv();
|
|
@@ -123,17 +123,17 @@ function resolveConfigPath(stateDir = resolveStateDir()) {
|
|
|
123
123
|
if (meta?.configPath && (!meta.stateDir || !stateDir || meta.stateDir === stateDir)) {
|
|
124
124
|
return meta.configPath;
|
|
125
125
|
}
|
|
126
|
-
return (0,
|
|
126
|
+
return (0, import_node_path5.join)(stateDir, "openclaw.json");
|
|
127
127
|
}
|
|
128
128
|
function resolveStateFile(filename) {
|
|
129
|
-
return (0,
|
|
129
|
+
return (0, import_node_path5.join)(resolveStateDir(), filename);
|
|
130
130
|
}
|
|
131
|
-
var
|
|
131
|
+
var import_node_fs6, import_node_path5;
|
|
132
132
|
var init_host = __esm({
|
|
133
133
|
"src/host.ts"() {
|
|
134
134
|
"use strict";
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
import_node_fs6 = require("fs");
|
|
136
|
+
import_node_path5 = require("path");
|
|
137
137
|
}
|
|
138
138
|
});
|
|
139
139
|
|
|
@@ -143,17 +143,17 @@ function credentialsPath() {
|
|
|
143
143
|
}
|
|
144
144
|
function readCredentials() {
|
|
145
145
|
const path2 = credentialsPath();
|
|
146
|
-
if (!(0,
|
|
146
|
+
if (!(0, import_node_fs7.existsSync)(path2)) return {};
|
|
147
147
|
try {
|
|
148
|
-
return JSON.parse((0,
|
|
148
|
+
return JSON.parse((0, import_node_fs7.readFileSync)(path2, "utf-8"));
|
|
149
149
|
} catch {
|
|
150
150
|
return {};
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
function writeCredentials(creds) {
|
|
154
154
|
const path2 = credentialsPath();
|
|
155
|
-
(0,
|
|
156
|
-
(0,
|
|
155
|
+
(0, import_node_fs7.mkdirSync)((0, import_node_path6.dirname)(path2), { recursive: true, mode: 448 });
|
|
156
|
+
(0, import_node_fs7.writeFileSync)(path2, JSON.stringify(creds, null, 2), {
|
|
157
157
|
encoding: "utf-8",
|
|
158
158
|
mode: 384
|
|
159
159
|
});
|
|
@@ -173,8 +173,8 @@ function requireApiKey() {
|
|
|
173
173
|
}
|
|
174
174
|
function watchCredentials(onChange) {
|
|
175
175
|
const path2 = credentialsPath();
|
|
176
|
-
const dir = (0,
|
|
177
|
-
const filename = (0,
|
|
176
|
+
const dir = (0, import_node_path6.dirname)(path2);
|
|
177
|
+
const filename = (0, import_node_path6.basename)(path2);
|
|
178
178
|
let debounceTimer = null;
|
|
179
179
|
const delayMs = 200;
|
|
180
180
|
const listener = (_event, changedName) => {
|
|
@@ -187,7 +187,7 @@ function watchCredentials(onChange) {
|
|
|
187
187
|
};
|
|
188
188
|
let watcher = null;
|
|
189
189
|
try {
|
|
190
|
-
watcher = (0,
|
|
190
|
+
watcher = (0, import_node_fs7.watch)(dir, { persistent: false }, listener);
|
|
191
191
|
} catch {
|
|
192
192
|
}
|
|
193
193
|
return () => {
|
|
@@ -195,12 +195,12 @@ function watchCredentials(onChange) {
|
|
|
195
195
|
watcher?.close();
|
|
196
196
|
};
|
|
197
197
|
}
|
|
198
|
-
var
|
|
198
|
+
var import_node_fs7, import_node_path6;
|
|
199
199
|
var init_credentials = __esm({
|
|
200
200
|
"src/auth/credentials.ts"() {
|
|
201
201
|
"use strict";
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
import_node_fs7 = require("fs");
|
|
203
|
+
import_node_path6 = require("path");
|
|
204
204
|
init_host();
|
|
205
205
|
}
|
|
206
206
|
});
|
|
@@ -208,8 +208,8 @@ var init_credentials = __esm({
|
|
|
208
208
|
// src/env.ts
|
|
209
209
|
function writeDotEnv(key, value) {
|
|
210
210
|
const path2 = resolveStateFile(".env");
|
|
211
|
-
(0,
|
|
212
|
-
const existing = (0,
|
|
211
|
+
(0, import_node_fs8.mkdirSync)((0, import_node_path7.dirname)(path2), { recursive: true });
|
|
212
|
+
const existing = (0, import_node_fs8.existsSync)(path2) ? (0, import_node_fs8.readFileSync)(path2, "utf-8") : "";
|
|
213
213
|
const lines = existing.split("\n");
|
|
214
214
|
const prefix = `${key}=`;
|
|
215
215
|
const idx = lines.findIndex((l) => l.startsWith(prefix));
|
|
@@ -220,13 +220,13 @@ function writeDotEnv(key, value) {
|
|
|
220
220
|
if (existing && !existing.endsWith("\n")) lines.push("");
|
|
221
221
|
lines.push(newLine);
|
|
222
222
|
}
|
|
223
|
-
(0,
|
|
223
|
+
(0, import_node_fs8.writeFileSync)(path2, lines.join("\n"), "utf-8");
|
|
224
224
|
}
|
|
225
225
|
function readDotEnv() {
|
|
226
226
|
const path2 = resolveStateFile(".env");
|
|
227
|
-
if (!(0,
|
|
227
|
+
if (!(0, import_node_fs8.existsSync)(path2)) return {};
|
|
228
228
|
return Object.fromEntries(
|
|
229
|
-
(0,
|
|
229
|
+
(0, import_node_fs8.readFileSync)(path2, "utf-8").split("\n").flatMap((line) => {
|
|
230
230
|
const eq = line.indexOf("=");
|
|
231
231
|
if (eq < 1) return [];
|
|
232
232
|
return [[line.slice(0, eq).trim(), line.slice(eq + 1).trim()]];
|
|
@@ -256,12 +256,12 @@ function getEnvUrls(env) {
|
|
|
256
256
|
function getAvailableEnvs() {
|
|
257
257
|
return Object.keys(ENV_CONFIG);
|
|
258
258
|
}
|
|
259
|
-
var
|
|
259
|
+
var import_node_fs8, import_node_path7, ENV_CONFIG, VALID_ENVS;
|
|
260
260
|
var init_env = __esm({
|
|
261
261
|
"src/env.ts"() {
|
|
262
262
|
"use strict";
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
import_node_fs8 = require("fs");
|
|
264
|
+
import_node_path7 = require("path");
|
|
265
265
|
init_credentials();
|
|
266
266
|
init_host();
|
|
267
267
|
ENV_CONFIG = {
|
|
@@ -5424,7 +5424,7 @@ function readBuildInjectedVersion() {
|
|
|
5424
5424
|
if (false) {
|
|
5425
5425
|
return void 0;
|
|
5426
5426
|
}
|
|
5427
|
-
const version = "1.
|
|
5427
|
+
const version = "1.11.0-beta.0".trim();
|
|
5428
5428
|
return version || void 0;
|
|
5429
5429
|
}
|
|
5430
5430
|
function readPluginVersionFromPackageJson() {
|
|
@@ -7432,6 +7432,12 @@ var LightRulesEvaluatorJob = class {
|
|
|
7432
7432
|
registry;
|
|
7433
7433
|
subagentRunner;
|
|
7434
7434
|
getNotificationsDir;
|
|
7435
|
+
/**
|
|
7436
|
+
* 轻量进程内评估器(事件驱动方案的正式路径)。
|
|
7437
|
+
* 配置后:每批通知走一次 pi-ai `complete()`,不再启动 agent session。
|
|
7438
|
+
* 未配置(或调用失败)时:回退 cron / subagent 老路径。
|
|
7439
|
+
*/
|
|
7440
|
+
inlineEvaluator;
|
|
7435
7441
|
/**
|
|
7436
7442
|
* 记录本进程生命周期内 job 是否已确认存在。
|
|
7437
7443
|
* 仅在 `ensureJobExists` 成功后置 true,避免每次 push 都做检查。
|
|
@@ -7450,22 +7456,40 @@ var LightRulesEvaluatorJob = class {
|
|
|
7450
7456
|
constructor(deps) {
|
|
7451
7457
|
this.logger = deps.logger;
|
|
7452
7458
|
this.registry = deps.registry;
|
|
7459
|
+
this.inlineEvaluator = deps.inlineEvaluator;
|
|
7453
7460
|
this.subagentRunner = deps.subagentRunner;
|
|
7454
7461
|
this.getNotificationsDir = deps.getNotificationsDir ?? (() => void 0);
|
|
7455
7462
|
}
|
|
7456
7463
|
/**
|
|
7457
7464
|
* 通知落盘后调用。若有新增通知且存在 enabled 规则,则触发评估。
|
|
7458
7465
|
*
|
|
7459
|
-
*
|
|
7460
|
-
*
|
|
7461
|
-
*
|
|
7466
|
+
* 路径优先级:
|
|
7467
|
+
* 1. inlineEvaluator(配置后的主路径):进程内一次 LLM 调用直接匹配 + 触发灯效
|
|
7468
|
+
* 2. cron.enqueueRun("force"):legacy,gateway context 路径
|
|
7469
|
+
* 3. subagentRunner.run:legacy,HTTP Relay 路径(cron 不可用时)
|
|
7462
7470
|
*
|
|
7463
|
-
*
|
|
7464
|
-
*
|
|
7471
|
+
* inline 成功(包括 0 命中)立刻返回,不会再走 legacy 路径。
|
|
7472
|
+
* inline 失败(LLM 错误 / 未配置 provider)才回退 legacy。
|
|
7473
|
+
*
|
|
7474
|
+
* @param cron 来自 gateway context 的 CronService;HTTP 路径下为 null
|
|
7475
|
+
* @param inserted 本次 ingest 实际新落盘的通知(StoredNotification 去重后)
|
|
7465
7476
|
*/
|
|
7466
|
-
async triggerIfNeeded(cron,
|
|
7467
|
-
if (
|
|
7477
|
+
async triggerIfNeeded(cron, inserted) {
|
|
7478
|
+
if (inserted.length === 0) return;
|
|
7468
7479
|
if (this.registry.getEnabled().length === 0) return;
|
|
7480
|
+
if (this.inlineEvaluator) {
|
|
7481
|
+
try {
|
|
7482
|
+
const ok2 = await this.inlineEvaluator.evaluate(inserted);
|
|
7483
|
+
if (ok2) return;
|
|
7484
|
+
this.logger.warn(
|
|
7485
|
+
"light-rules-evaluator: inline evaluator failed; falling back to legacy agent session path"
|
|
7486
|
+
);
|
|
7487
|
+
} catch (err2) {
|
|
7488
|
+
this.logger.warn(
|
|
7489
|
+
`light-rules-evaluator: inline evaluator threw: ${err2?.message ?? err2}; falling back`
|
|
7490
|
+
);
|
|
7491
|
+
}
|
|
7492
|
+
}
|
|
7469
7493
|
if (!cron) {
|
|
7470
7494
|
await this.triggerViaSubagent();
|
|
7471
7495
|
return;
|
|
@@ -7570,9 +7594,141 @@ var LightRulesEvaluatorJob = class {
|
|
|
7570
7594
|
}
|
|
7571
7595
|
};
|
|
7572
7596
|
|
|
7597
|
+
// src/light-rules/inline-evaluator.ts
|
|
7598
|
+
init_credentials();
|
|
7599
|
+
|
|
7600
|
+
// src/light/sender.ts
|
|
7601
|
+
var import_node_crypto2 = require("crypto");
|
|
7602
|
+
init_env();
|
|
7603
|
+
async function sendLightEffect(apiKey, segments, logger, repeatInput, reason) {
|
|
7604
|
+
const apiUrl = getEnvUrls().lightApiUrl;
|
|
7605
|
+
const appKey = "7Q617S1G5WD274JI";
|
|
7606
|
+
const templateId = "1990771146010017788";
|
|
7607
|
+
logger?.info(
|
|
7608
|
+
`Light sender: apiUrl=${apiUrl ?? "UNSET"}, appKey=${appKey ? appKey.substring(0, 8) + "\u2026" : "UNSET"}, templateId=${templateId ?? "UNSET"}, apiKey=${apiKey ? apiKey.substring(0, 20) + "\u2026" : "EMPTY"}, segments=${JSON.stringify(segments)}`
|
|
7609
|
+
);
|
|
7610
|
+
if (!apiUrl || !appKey || !templateId) {
|
|
7611
|
+
return {
|
|
7612
|
+
ok: false,
|
|
7613
|
+
error: "\u706F\u6548 API \u672A\u914D\u7F6E\uFF0C\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF LIGHT_API_URL / LIGHT_APP_KEY / LIGHT_TEMPLATE_ID"
|
|
7614
|
+
};
|
|
7615
|
+
}
|
|
7616
|
+
let bizContent;
|
|
7617
|
+
try {
|
|
7618
|
+
bizContent = buildLightEffectApnsBody(segments, repeatInput);
|
|
7619
|
+
} catch (error) {
|
|
7620
|
+
return { ok: false, error: error?.message ?? String(error) };
|
|
7621
|
+
}
|
|
7622
|
+
const bizUniqueId = (0, import_node_crypto2.randomUUID)();
|
|
7623
|
+
const requestBody = {
|
|
7624
|
+
appKey,
|
|
7625
|
+
bizMap: { noticeType: "APP_NOTIFICATION_IMPORTANT", reason },
|
|
7626
|
+
bizUniqueId,
|
|
7627
|
+
paramsMap: { bizContent },
|
|
7628
|
+
pushType: "SPECIFY_PUSH",
|
|
7629
|
+
templateId
|
|
7630
|
+
};
|
|
7631
|
+
logger?.info(
|
|
7632
|
+
`Light sender: POST ${apiUrl}, bizUniqueId=${bizUniqueId}, body=${JSON.stringify(requestBody).substring(0, 500)}`
|
|
7633
|
+
);
|
|
7634
|
+
const res = await fetch(apiUrl, {
|
|
7635
|
+
method: "POST",
|
|
7636
|
+
headers: {
|
|
7637
|
+
"Content-Type": "application/json",
|
|
7638
|
+
"X-Api-Key-Id": apiKey.startsWith("Bearer ") ? apiKey.slice("Bearer ".length) : apiKey
|
|
7639
|
+
},
|
|
7640
|
+
body: JSON.stringify(requestBody)
|
|
7641
|
+
});
|
|
7642
|
+
const resBody = await res.text();
|
|
7643
|
+
if (!res.ok) {
|
|
7644
|
+
logger?.warn(
|
|
7645
|
+
`Light sender: FAILED ${res.status}, url=${apiUrl}, resBody=${resBody.substring(0, 500)}`
|
|
7646
|
+
);
|
|
7647
|
+
return { ok: false, status: res.status, error: resBody };
|
|
7648
|
+
}
|
|
7649
|
+
logger?.info(`Light sender: OK bizUniqueId=${bizUniqueId}, resBody=${resBody.substring(0, 200)}`);
|
|
7650
|
+
return { ok: true, bizUniqueId, response: JSON.parse(resBody) };
|
|
7651
|
+
}
|
|
7652
|
+
|
|
7653
|
+
// src/light-rules/inline-evaluator.ts
|
|
7654
|
+
var InlineLightRuleEvaluator = class {
|
|
7655
|
+
logger;
|
|
7656
|
+
registry;
|
|
7657
|
+
invoker;
|
|
7658
|
+
constructor(deps) {
|
|
7659
|
+
this.logger = deps.logger;
|
|
7660
|
+
this.registry = deps.registry;
|
|
7661
|
+
this.invoker = deps.invoker;
|
|
7662
|
+
}
|
|
7663
|
+
/**
|
|
7664
|
+
* 评估一批新通知。
|
|
7665
|
+
*
|
|
7666
|
+
* @returns true 表示评估流程正常结束;false 表示 invoker 失败,调用方可选择回退。
|
|
7667
|
+
*/
|
|
7668
|
+
async evaluate(notifications) {
|
|
7669
|
+
if (notifications.length === 0) return true;
|
|
7670
|
+
const rules = this.registry.getEnabled();
|
|
7671
|
+
if (rules.length === 0) return true;
|
|
7672
|
+
const matches = await this.invoker.matchNotifications(notifications, rules);
|
|
7673
|
+
if (matches === null) return false;
|
|
7674
|
+
if (matches.length === 0) {
|
|
7675
|
+
this.logger.info(
|
|
7676
|
+
`lightrules: 0 matches (notifications=${notifications.length}, rules=${rules.length})`
|
|
7677
|
+
);
|
|
7678
|
+
return true;
|
|
7679
|
+
}
|
|
7680
|
+
const firedRules = /* @__PURE__ */ new Set();
|
|
7681
|
+
for (const match of matches) {
|
|
7682
|
+
if (firedRules.has(match.ruleName)) continue;
|
|
7683
|
+
const rule = this.registry.get(match.ruleName);
|
|
7684
|
+
if (!rule) {
|
|
7685
|
+
this.logger.warn(
|
|
7686
|
+
`lightrules: matched rule '${match.ruleName}' not found in registry`
|
|
7687
|
+
);
|
|
7688
|
+
continue;
|
|
7689
|
+
}
|
|
7690
|
+
firedRules.add(match.ruleName);
|
|
7691
|
+
await this.triggerLight(rule);
|
|
7692
|
+
}
|
|
7693
|
+
this.logger.info(
|
|
7694
|
+
`lightrules: fired ${firedRules.size} rule(s): ${[...firedRules].join(", ")} (from ${matches.length} match(es) across ${notifications.length} notification(s))`
|
|
7695
|
+
);
|
|
7696
|
+
return true;
|
|
7697
|
+
}
|
|
7698
|
+
async triggerLight(rule) {
|
|
7699
|
+
let apiKey;
|
|
7700
|
+
try {
|
|
7701
|
+
apiKey = requireApiKey();
|
|
7702
|
+
} catch (err2) {
|
|
7703
|
+
this.logger.warn(
|
|
7704
|
+
`lightrules: trigger '${rule.name}' skipped - no API key: ${err2?.message ?? err2}`
|
|
7705
|
+
);
|
|
7706
|
+
return;
|
|
7707
|
+
}
|
|
7708
|
+
try {
|
|
7709
|
+
const result = await sendLightEffect(
|
|
7710
|
+
apiKey,
|
|
7711
|
+
rule.segments,
|
|
7712
|
+
this.logger,
|
|
7713
|
+
{ repeat_times: rule.repeat_times },
|
|
7714
|
+
rule.description
|
|
7715
|
+
);
|
|
7716
|
+
if (!result.ok) {
|
|
7717
|
+
this.logger.warn(
|
|
7718
|
+
`lightrules: trigger '${rule.name}' http-failed (${result.status ?? "?"}): ${result.error}`
|
|
7719
|
+
);
|
|
7720
|
+
}
|
|
7721
|
+
} catch (err2) {
|
|
7722
|
+
this.logger.warn(
|
|
7723
|
+
`lightrules: trigger '${rule.name}' exception: ${err2?.message ?? err2}`
|
|
7724
|
+
);
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
};
|
|
7728
|
+
|
|
7573
7729
|
// src/light-rules/migration.ts
|
|
7574
|
-
var
|
|
7575
|
-
var
|
|
7730
|
+
var import_node_fs9 = require("fs");
|
|
7731
|
+
var import_node_path8 = require("path");
|
|
7576
7732
|
var NO_MATCH_FETCH_PY = `#!/usr/bin/env python3
|
|
7577
7733
|
# \u6B64\u6587\u4EF6\u7531\u8FC1\u79FB\u5DE5\u5177\u751F\u6210\u3002
|
|
7578
7734
|
# \u706F\u6548\u89C4\u5219\u5DF2\u8FC1\u79FB\u81F3\u4E8B\u4EF6\u9A71\u52A8\u67B6\u6784\uFF0C\u6B64 cron job \u4E0D\u518D\u6267\u884C\u5B9E\u9645\u5DE5\u4F5C\u3002
|
|
@@ -7582,32 +7738,32 @@ function normalizeScriptText(text) {
|
|
|
7582
7738
|
return text.replace(/\r\n/g, "\n").trim();
|
|
7583
7739
|
}
|
|
7584
7740
|
function resolveTasksDir(ctx) {
|
|
7585
|
-
if (ctx.workspaceDir) return (0,
|
|
7741
|
+
if (ctx.workspaceDir) return (0, import_node_path8.join)(ctx.workspaceDir, "tasks");
|
|
7586
7742
|
if (ctx.stateDir) {
|
|
7587
|
-
const inferredWorkspaceDir = (0,
|
|
7588
|
-
if ((0,
|
|
7589
|
-
return (0,
|
|
7743
|
+
const inferredWorkspaceDir = (0, import_node_path8.join)(ctx.stateDir, "workspace");
|
|
7744
|
+
if ((0, import_node_fs9.existsSync)(inferredWorkspaceDir)) return (0, import_node_path8.join)(inferredWorkspaceDir, "tasks");
|
|
7745
|
+
return (0, import_node_path8.join)(ctx.stateDir, "tasks");
|
|
7590
7746
|
}
|
|
7591
7747
|
return null;
|
|
7592
7748
|
}
|
|
7593
7749
|
function migrateLegacyLightRuleTasks(ctx, logger) {
|
|
7594
7750
|
const tasksDir3 = resolveTasksDir(ctx);
|
|
7595
|
-
if (!tasksDir3 || !(0,
|
|
7751
|
+
if (!tasksDir3 || !(0, import_node_fs9.existsSync)(tasksDir3)) return;
|
|
7596
7752
|
try {
|
|
7597
|
-
for (const entry of (0,
|
|
7753
|
+
for (const entry of (0, import_node_fs9.readdirSync)(tasksDir3, { withFileTypes: true })) {
|
|
7598
7754
|
if (!entry.isDirectory()) continue;
|
|
7599
|
-
migrateTaskDir((0,
|
|
7755
|
+
migrateTaskDir((0, import_node_path8.join)(tasksDir3, String(entry.name)), logger);
|
|
7600
7756
|
}
|
|
7601
7757
|
} catch (err2) {
|
|
7602
7758
|
logger.warn(`migration: failed to read tasks dir: ${err2?.message}`);
|
|
7603
7759
|
}
|
|
7604
7760
|
}
|
|
7605
7761
|
function migrateTaskDir(taskDir, logger) {
|
|
7606
|
-
const metaPath = (0,
|
|
7607
|
-
if (!(0,
|
|
7762
|
+
const metaPath = (0, import_node_path8.join)(taskDir, "meta.json");
|
|
7763
|
+
if (!(0, import_node_fs9.existsSync)(metaPath)) return;
|
|
7608
7764
|
let meta;
|
|
7609
7765
|
try {
|
|
7610
|
-
meta = JSON.parse((0,
|
|
7766
|
+
meta = JSON.parse((0, import_node_fs9.readFileSync)(metaPath, "utf-8"));
|
|
7611
7767
|
} catch {
|
|
7612
7768
|
return;
|
|
7613
7769
|
}
|
|
@@ -7616,7 +7772,7 @@ function migrateTaskDir(taskDir, logger) {
|
|
|
7616
7772
|
mergeMatchRulesIntoDescription(meta, name, metaPath, logger);
|
|
7617
7773
|
replaceFetchPy(taskDir, name, logger);
|
|
7618
7774
|
for (const filename of ["README.md", "checkpoint.json"]) {
|
|
7619
|
-
removeFile((0,
|
|
7775
|
+
removeFile((0, import_node_path8.join)(taskDir, filename), name, filename, logger);
|
|
7620
7776
|
}
|
|
7621
7777
|
}
|
|
7622
7778
|
function mergeMatchRulesIntoDescription(meta, name, metaPath, logger) {
|
|
@@ -7637,36 +7793,185 @@ function mergeMatchRulesIntoDescription(meta, name, metaPath, logger) {
|
|
|
7637
7793
|
}
|
|
7638
7794
|
delete meta.matchRules;
|
|
7639
7795
|
try {
|
|
7640
|
-
(0,
|
|
7796
|
+
(0, import_node_fs9.writeFileSync)(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
7641
7797
|
logger.info(`migration: merged matchRules into description for light rule: ${name}`);
|
|
7642
7798
|
} catch (err2) {
|
|
7643
7799
|
logger.warn(`migration: failed to update meta.json for ${name}: ${err2?.message}`);
|
|
7644
7800
|
}
|
|
7645
7801
|
}
|
|
7646
7802
|
function replaceFetchPy(taskDir, name, logger) {
|
|
7647
|
-
const fetchPyPath = (0,
|
|
7648
|
-
if (!(0,
|
|
7803
|
+
const fetchPyPath = (0, import_node_path8.join)(taskDir, "fetch.py");
|
|
7804
|
+
if (!(0, import_node_fs9.existsSync)(fetchPyPath)) return;
|
|
7649
7805
|
try {
|
|
7650
|
-
const existing = (0,
|
|
7806
|
+
const existing = (0, import_node_fs9.readFileSync)(fetchPyPath, "utf-8");
|
|
7651
7807
|
if (normalizeScriptText(existing) === normalizeScriptText(NO_MATCH_FETCH_PY)) {
|
|
7652
7808
|
return;
|
|
7653
7809
|
}
|
|
7654
|
-
(0,
|
|
7810
|
+
(0, import_node_fs9.writeFileSync)(fetchPyPath, NO_MATCH_FETCH_PY, "utf-8");
|
|
7655
7811
|
logger.info(`migration: replaced fetch.py with NO_MATCH placeholder for ${name}`);
|
|
7656
7812
|
} catch (err2) {
|
|
7657
7813
|
logger.warn(`migration: failed to replace fetch.py for ${name}: ${err2?.message}`);
|
|
7658
7814
|
}
|
|
7659
7815
|
}
|
|
7660
7816
|
function removeFile(filePath, ruleName, filename, logger) {
|
|
7661
|
-
if (!(0,
|
|
7817
|
+
if (!(0, import_node_fs9.existsSync)(filePath)) return;
|
|
7662
7818
|
try {
|
|
7663
|
-
(0,
|
|
7819
|
+
(0, import_node_fs9.rmSync)(filePath);
|
|
7664
7820
|
logger.info(`migration: removed ${filename} for light rule: ${ruleName}`);
|
|
7665
7821
|
} catch (err2) {
|
|
7666
7822
|
logger.warn(`migration: failed to remove ${filename} for ${ruleName}: ${err2?.message}`);
|
|
7667
7823
|
}
|
|
7668
7824
|
}
|
|
7669
7825
|
|
|
7826
|
+
// src/light-rules/pi-invoker.ts
|
|
7827
|
+
var import_agent_runtime = require("openclaw/plugin-sdk/agent-runtime");
|
|
7828
|
+
var DEFAULT_PROVIDER = "anthropic";
|
|
7829
|
+
var DEFAULT_MODEL_ID = "claude-haiku-4-5-20251001";
|
|
7830
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
7831
|
+
var MAX_OUTPUT_TOKENS = 512;
|
|
7832
|
+
var PiAiInvoker = class {
|
|
7833
|
+
constructor(api, logger, options = {}) {
|
|
7834
|
+
this.api = api;
|
|
7835
|
+
this.logger = logger;
|
|
7836
|
+
this.options = options;
|
|
7837
|
+
}
|
|
7838
|
+
/**
|
|
7839
|
+
* 对一批通知和当前启用的规则做一次 LLM 匹配。
|
|
7840
|
+
*
|
|
7841
|
+
* 返回:
|
|
7842
|
+
* - `LlmMatchResult[]`:匹配成功(可能为空数组,即 0 命中)
|
|
7843
|
+
* - `null`:调用失败(模型准备失败 / 网络 / 解析)。调用方据此决定是否回退。
|
|
7844
|
+
*/
|
|
7845
|
+
async matchNotifications(notifications, rules) {
|
|
7846
|
+
if (notifications.length === 0 || rules.length === 0) return [];
|
|
7847
|
+
const provider = this.options.provider ?? DEFAULT_PROVIDER;
|
|
7848
|
+
const modelId = this.options.modelId ?? DEFAULT_MODEL_ID;
|
|
7849
|
+
const prepared = await (0, import_agent_runtime.prepareSimpleCompletionModel)({
|
|
7850
|
+
cfg: this.api.config,
|
|
7851
|
+
provider,
|
|
7852
|
+
modelId
|
|
7853
|
+
});
|
|
7854
|
+
if ("error" in prepared) {
|
|
7855
|
+
this.logger.warn(
|
|
7856
|
+
`PiAiInvoker: prepare ${provider}/${modelId} failed: ${prepared.error}`
|
|
7857
|
+
);
|
|
7858
|
+
return null;
|
|
7859
|
+
}
|
|
7860
|
+
const systemPrompt = buildSystemPrompt(rules);
|
|
7861
|
+
const userMessage = buildUserMessage(notifications);
|
|
7862
|
+
try {
|
|
7863
|
+
const resp = await (0, import_agent_runtime.completeWithPreparedSimpleCompletionModel)({
|
|
7864
|
+
model: prepared.model,
|
|
7865
|
+
auth: prepared.auth,
|
|
7866
|
+
context: {
|
|
7867
|
+
systemPrompt,
|
|
7868
|
+
messages: [
|
|
7869
|
+
{ role: "user", content: userMessage, timestamp: Date.now() }
|
|
7870
|
+
]
|
|
7871
|
+
},
|
|
7872
|
+
options: {
|
|
7873
|
+
temperature: 0,
|
|
7874
|
+
maxTokens: MAX_OUTPUT_TOKENS,
|
|
7875
|
+
signal: AbortSignal.timeout(this.options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
7876
|
+
cacheRetention: "short"
|
|
7877
|
+
}
|
|
7878
|
+
});
|
|
7879
|
+
if (resp.stopReason === "error" || resp.stopReason === "aborted") {
|
|
7880
|
+
this.logger.warn(
|
|
7881
|
+
`PiAiInvoker: complete stopped with ${resp.stopReason}: ${resp.errorMessage ?? "n/a"}`
|
|
7882
|
+
);
|
|
7883
|
+
return null;
|
|
7884
|
+
}
|
|
7885
|
+
const text = extractText(resp.content);
|
|
7886
|
+
return parseMatchResult(text, notifications.length, rules);
|
|
7887
|
+
} catch (err2) {
|
|
7888
|
+
this.logger.warn(`PiAiInvoker: complete failed: ${err2?.message ?? err2}`);
|
|
7889
|
+
return null;
|
|
7890
|
+
}
|
|
7891
|
+
}
|
|
7892
|
+
};
|
|
7893
|
+
function extractText(content) {
|
|
7894
|
+
if (!Array.isArray(content)) return "";
|
|
7895
|
+
const parts = [];
|
|
7896
|
+
for (const c of content) {
|
|
7897
|
+
if (c && typeof c === "object" && c.type === "text") {
|
|
7898
|
+
const t = c.text;
|
|
7899
|
+
if (typeof t === "string") parts.push(t);
|
|
7900
|
+
}
|
|
7901
|
+
}
|
|
7902
|
+
return parts.join("");
|
|
7903
|
+
}
|
|
7904
|
+
function buildSystemPrompt(rules) {
|
|
7905
|
+
const lines = [
|
|
7906
|
+
'\u4F60\u662F"\u624B\u673A\u901A\u77E5 \u2192 \u706F\u6548\u89C4\u5219"\u547D\u4E2D\u5339\u914D\u52A9\u624B\u3002',
|
|
7907
|
+
"",
|
|
7908
|
+
"\u5F53\u524D\u542F\u7528\u7684\u706F\u6548\u89C4\u5219\uFF08\u6309\u521B\u5EFA\u65F6\u95F4\u5012\u5E8F\uFF09\uFF1A",
|
|
7909
|
+
""
|
|
7910
|
+
];
|
|
7911
|
+
const sorted = [...rules].sort(
|
|
7912
|
+
(a, b) => (b.createdAt ?? "").localeCompare(a.createdAt ?? "")
|
|
7913
|
+
);
|
|
7914
|
+
sorted.forEach((rule, idx) => {
|
|
7915
|
+
lines.push(`[${idx + 1}] name: ${rule.name}`);
|
|
7916
|
+
lines.push(` description: ${rule.description}`);
|
|
7917
|
+
lines.push("");
|
|
7918
|
+
});
|
|
7919
|
+
lines.push(
|
|
7920
|
+
"\u4EFB\u52A1\uFF1A\u5BF9\u7528\u6237\u63A5\u4E0B\u6765\u53D1\u6765\u7684\u6BCF\u6761\u901A\u77E5\uFF0C\u5224\u65AD\u5B83\u547D\u4E2D\u4E86\u54EA\u4E9B\u89C4\u5219\u3002",
|
|
7921
|
+
"",
|
|
7922
|
+
"\u8F93\u51FA\u5FC5\u987B\u662F\u7EAF JSON\uFF08\u4E0D\u8981 Markdown \u4EE3\u7801\u5757\u3001\u4E0D\u8981\u4EFB\u4F55\u89E3\u91CA\u6587\u5B57\uFF09\uFF1A",
|
|
7923
|
+
'{"matches":[{"i":<\u901A\u77E5\u4E0B\u6807>,"rule":"<\u89C4\u5219name>"}]}',
|
|
7924
|
+
"",
|
|
7925
|
+
'- \u547D\u4E2D 0 \u6761 \u2192 \u8F93\u51FA {"matches":[]}',
|
|
7926
|
+
"- \u4E00\u6761\u901A\u77E5\u53EF\u80FD\u547D\u4E2D\u591A\u6761\u89C4\u5219 \u2192 \u6BCF\u4E2A\u7EC4\u5408\u4E00\u4E2A\u6761\u76EE",
|
|
7927
|
+
'- \u4E25\u683C\u6309\u89C4\u5219 description \u5224\u65AD\uFF0C\u62FF\u4E0D\u51C6\u65F6\u503E\u5411\u4E8E"\u4E0D\u89E6\u53D1"',
|
|
7928
|
+
"- rule \u5FC5\u987B\u7CBE\u786E\u7B49\u4E8E\u4E0A\u9762\u5217\u51FA\u7684 name \u5B57\u7B26\u4E32"
|
|
7929
|
+
);
|
|
7930
|
+
return lines.join("\n");
|
|
7931
|
+
}
|
|
7932
|
+
function buildUserMessage(notifications) {
|
|
7933
|
+
const lines = [
|
|
7934
|
+
`\u4EE5\u4E0B ${notifications.length} \u6761\u65B0\u624B\u673A\u901A\u77E5\uFF0C\u6309\u987A\u5E8F\u7F16\u53F7\uFF1A`,
|
|
7935
|
+
""
|
|
7936
|
+
];
|
|
7937
|
+
notifications.forEach((n, i) => {
|
|
7938
|
+
const app = n.appDisplayName ?? n.appName;
|
|
7939
|
+
lines.push(
|
|
7940
|
+
`[${i}] app=${app} time=${n.timestamp}`,
|
|
7941
|
+
` title: ${n.title}`,
|
|
7942
|
+
` content: ${n.content}`,
|
|
7943
|
+
""
|
|
7944
|
+
);
|
|
7945
|
+
});
|
|
7946
|
+
lines.push("\u8BF7\u5224\u65AD\u6BCF\u6761\u901A\u77E5\u547D\u4E2D\u4E86\u54EA\u4E9B\u89C4\u5219\uFF0C\u6309\u7EA6\u5B9A\u683C\u5F0F\u8F93\u51FA JSON\u3002");
|
|
7947
|
+
return lines.join("\n");
|
|
7948
|
+
}
|
|
7949
|
+
function parseMatchResult(rawText, notificationCount, rules) {
|
|
7950
|
+
if (!rawText) return [];
|
|
7951
|
+
const text = rawText.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
7952
|
+
let parsed;
|
|
7953
|
+
try {
|
|
7954
|
+
parsed = JSON.parse(text);
|
|
7955
|
+
} catch {
|
|
7956
|
+
return [];
|
|
7957
|
+
}
|
|
7958
|
+
if (!parsed || typeof parsed !== "object") return [];
|
|
7959
|
+
const raw = parsed.matches;
|
|
7960
|
+
if (!Array.isArray(raw)) return [];
|
|
7961
|
+
const validRuleNames = new Set(rules.map((r) => r.name));
|
|
7962
|
+
const results = [];
|
|
7963
|
+
for (const m of raw) {
|
|
7964
|
+
if (!m || typeof m !== "object") continue;
|
|
7965
|
+
const entry = m;
|
|
7966
|
+
const i = typeof entry.i === "number" ? entry.i : typeof entry.index === "number" ? entry.index : -1;
|
|
7967
|
+
const ruleName = typeof entry.rule === "string" ? entry.rule : null;
|
|
7968
|
+
if (!Number.isInteger(i) || i < 0 || i >= notificationCount) continue;
|
|
7969
|
+
if (!ruleName || !validRuleNames.has(ruleName)) continue;
|
|
7970
|
+
results.push({ notificationIndex: i, ruleName });
|
|
7971
|
+
}
|
|
7972
|
+
return results;
|
|
7973
|
+
}
|
|
7974
|
+
|
|
7670
7975
|
// src/plugin/auto-update.ts
|
|
7671
7976
|
init_env();
|
|
7672
7977
|
|
|
@@ -8515,61 +8820,6 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
8515
8820
|
|
|
8516
8821
|
// src/cli/light-send.ts
|
|
8517
8822
|
init_credentials();
|
|
8518
|
-
|
|
8519
|
-
// src/light/sender.ts
|
|
8520
|
-
var import_node_crypto2 = require("crypto");
|
|
8521
|
-
init_env();
|
|
8522
|
-
async function sendLightEffect(apiKey, segments, logger, repeatInput, reason) {
|
|
8523
|
-
const apiUrl = getEnvUrls().lightApiUrl;
|
|
8524
|
-
const appKey = "7Q617S1G5WD274JI";
|
|
8525
|
-
const templateId = "1990771146010017788";
|
|
8526
|
-
logger?.info(
|
|
8527
|
-
`Light sender: apiUrl=${apiUrl ?? "UNSET"}, appKey=${appKey ? appKey.substring(0, 8) + "\u2026" : "UNSET"}, templateId=${templateId ?? "UNSET"}, apiKey=${apiKey ? apiKey.substring(0, 20) + "\u2026" : "EMPTY"}, segments=${JSON.stringify(segments)}`
|
|
8528
|
-
);
|
|
8529
|
-
if (!apiUrl || !appKey || !templateId) {
|
|
8530
|
-
return {
|
|
8531
|
-
ok: false,
|
|
8532
|
-
error: "\u706F\u6548 API \u672A\u914D\u7F6E\uFF0C\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF LIGHT_API_URL / LIGHT_APP_KEY / LIGHT_TEMPLATE_ID"
|
|
8533
|
-
};
|
|
8534
|
-
}
|
|
8535
|
-
let bizContent;
|
|
8536
|
-
try {
|
|
8537
|
-
bizContent = buildLightEffectApnsBody(segments, repeatInput);
|
|
8538
|
-
} catch (error) {
|
|
8539
|
-
return { ok: false, error: error?.message ?? String(error) };
|
|
8540
|
-
}
|
|
8541
|
-
const bizUniqueId = (0, import_node_crypto2.randomUUID)();
|
|
8542
|
-
const requestBody = {
|
|
8543
|
-
appKey,
|
|
8544
|
-
bizMap: { noticeType: "APP_NOTIFICATION_IMPORTANT", reason },
|
|
8545
|
-
bizUniqueId,
|
|
8546
|
-
paramsMap: { bizContent },
|
|
8547
|
-
pushType: "SPECIFY_PUSH",
|
|
8548
|
-
templateId
|
|
8549
|
-
};
|
|
8550
|
-
logger?.info(
|
|
8551
|
-
`Light sender: POST ${apiUrl}, bizUniqueId=${bizUniqueId}, body=${JSON.stringify(requestBody).substring(0, 500)}`
|
|
8552
|
-
);
|
|
8553
|
-
const res = await fetch(apiUrl, {
|
|
8554
|
-
method: "POST",
|
|
8555
|
-
headers: {
|
|
8556
|
-
"Content-Type": "application/json",
|
|
8557
|
-
"X-Api-Key-Id": apiKey.startsWith("Bearer ") ? apiKey.slice("Bearer ".length) : apiKey
|
|
8558
|
-
},
|
|
8559
|
-
body: JSON.stringify(requestBody)
|
|
8560
|
-
});
|
|
8561
|
-
const resBody = await res.text();
|
|
8562
|
-
if (!res.ok) {
|
|
8563
|
-
logger?.warn(
|
|
8564
|
-
`Light sender: FAILED ${res.status}, url=${apiUrl}, resBody=${resBody.substring(0, 500)}`
|
|
8565
|
-
);
|
|
8566
|
-
return { ok: false, status: res.status, error: resBody };
|
|
8567
|
-
}
|
|
8568
|
-
logger?.info(`Light sender: OK bizUniqueId=${bizUniqueId}, resBody=${resBody.substring(0, 200)}`);
|
|
8569
|
-
return { ok: true, bizUniqueId, response: JSON.parse(resBody) };
|
|
8570
|
-
}
|
|
8571
|
-
|
|
8572
|
-
// src/cli/light-send.ts
|
|
8573
8823
|
function registerLightSend(light) {
|
|
8574
8824
|
light.command("send").description("\u53D1\u9001\u706F\u6548\u6307\u4EE4\u5230\u786C\u4EF6\u8BBE\u5907").requiredOption("--segments <json>", "\u706F\u6548\u53C2\u6570 JSON").option("--repeat", "\u65E0\u9650\u5FAA\u73AF\u64AD\u653E\uFF08\u9ED8\u8BA4\u4EC5\u64AD\u653E\u4E00\u8F6E\uFF09").option("--repeat-times <n>", "\u6574\u6761\u7EC4\u5408\u91CD\u590D\u6B21\u6570\uFF1A0=\u65E0\u9650\uFF0C1=\u4E00\u8F6E\uFF1B\u5F53\u524D ANCS \u8DEF\u5F84\u4E0D\u652F\u6301 >=2").action(async (opts) => {
|
|
8575
8825
|
let apiKey;
|
|
@@ -10590,11 +10840,11 @@ var import_node_fs25 = require("fs");
|
|
|
10590
10840
|
var import_node_path21 = require("path");
|
|
10591
10841
|
var import_promises2 = require("stream/promises");
|
|
10592
10842
|
var import_node_stream = require("stream");
|
|
10593
|
-
var
|
|
10843
|
+
var DEFAULT_TIMEOUT_MS2 = 5 * 60 * 1e3;
|
|
10594
10844
|
var DEFAULT_MAX_RETRIES = 3;
|
|
10595
10845
|
var DEFAULT_RETRY_BACKOFF_MS = 2e3;
|
|
10596
10846
|
async function downloadFile(url, destPath, logger, options) {
|
|
10597
|
-
const timeoutMs = options?.timeoutMs ??
|
|
10847
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
10598
10848
|
const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
10599
10849
|
const retryBackoffMs = options?.retryBackoffMs ?? DEFAULT_RETRY_BACKOFF_MS;
|
|
10600
10850
|
(0, import_node_fs25.mkdirSync)((0, import_node_path21.dirname)(destPath), { recursive: true });
|
|
@@ -10879,7 +11129,7 @@ var import_node_path24 = require("path");
|
|
|
10879
11129
|
// node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
|
|
10880
11130
|
var import_stream = __toESM(require_stream(), 1);
|
|
10881
11131
|
var import_receiver = __toESM(require_receiver(), 1);
|
|
10882
|
-
var
|
|
11132
|
+
var import_sender4 = __toESM(require_sender(), 1);
|
|
10883
11133
|
var import_websocket = __toESM(require_websocket(), 1);
|
|
10884
11134
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
10885
11135
|
var wrapper_default = import_websocket.default;
|
|
@@ -12604,9 +12854,9 @@ function registerNotificationInterfaces(deps) {
|
|
|
12604
12854
|
onAfterIngest,
|
|
12605
12855
|
getCronForHttpIngest
|
|
12606
12856
|
} = deps;
|
|
12607
|
-
function triggerAfterIngest(
|
|
12608
|
-
if (
|
|
12609
|
-
void Promise.resolve().then(() => onAfterIngest(
|
|
12857
|
+
function triggerAfterIngest(inserted, cron) {
|
|
12858
|
+
if (inserted.length === 0 || !onAfterIngest) return;
|
|
12859
|
+
void Promise.resolve().then(() => onAfterIngest(inserted, cron)).catch((err2) => logger.warn(`onAfterIngest failed: ${err2?.message ?? err2}`));
|
|
12610
12860
|
}
|
|
12611
12861
|
registerGatewayMethod(
|
|
12612
12862
|
"notifications.push",
|
|
@@ -12630,7 +12880,7 @@ function registerNotificationInterfaces(deps) {
|
|
|
12630
12880
|
const filtered = filterNotifications(items);
|
|
12631
12881
|
const result = filtered.length ? await storage.ingest(filtered) : createEmptyIngestResult();
|
|
12632
12882
|
respond(true, toIngestResponse(result));
|
|
12633
|
-
triggerAfterIngest(result.inserted
|
|
12883
|
+
triggerAfterIngest(result.inserted, context?.cron);
|
|
12634
12884
|
}
|
|
12635
12885
|
);
|
|
12636
12886
|
api.registerHttpRoute({
|
|
@@ -12674,7 +12924,7 @@ function registerNotificationInterfaces(deps) {
|
|
|
12674
12924
|
const result = filtered.length ? await storage.ingest(filtered) : createEmptyIngestResult();
|
|
12675
12925
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
12676
12926
|
res.end(JSON.stringify({ ok: true, ...toIngestResponse(result) }));
|
|
12677
|
-
triggerAfterIngest(result.inserted
|
|
12927
|
+
triggerAfterIngest(result.inserted, getCronForHttpIngest?.());
|
|
12678
12928
|
}
|
|
12679
12929
|
});
|
|
12680
12930
|
logger.info("Gateway \u901A\u77E5\u65B9\u6CD5\u5DF2\u6CE8\u518C: notifications.push");
|
|
@@ -13218,9 +13468,16 @@ var index_default = {
|
|
|
13218
13468
|
broadcastFn("recording.status", event);
|
|
13219
13469
|
}
|
|
13220
13470
|
const lightRuleRegistry = new LightRuleRegistry(lightRuleCtx);
|
|
13471
|
+
const lightRuleInvoker = new PiAiInvoker(api, logger);
|
|
13472
|
+
const inlineLightRuleEvaluator = new InlineLightRuleEvaluator({
|
|
13473
|
+
logger,
|
|
13474
|
+
registry: lightRuleRegistry,
|
|
13475
|
+
invoker: lightRuleInvoker
|
|
13476
|
+
});
|
|
13221
13477
|
const lightRulesEvaluatorJob = new LightRulesEvaluatorJob({
|
|
13222
13478
|
logger,
|
|
13223
13479
|
registry: lightRuleRegistry,
|
|
13480
|
+
inlineEvaluator: inlineLightRuleEvaluator,
|
|
13224
13481
|
subagentRunner: api.runtime.subagent,
|
|
13225
13482
|
getNotificationsDir: () => openclawDir ? getStateFallbackNotificationDir(openclawDir) : void 0
|
|
13226
13483
|
});
|
|
@@ -13253,8 +13510,8 @@ var index_default = {
|
|
|
13253
13510
|
filterNotifications,
|
|
13254
13511
|
registerGatewayMethod: registerGatewayMethodWithBroadcastCapture,
|
|
13255
13512
|
tunnelService,
|
|
13256
|
-
onAfterIngest: (
|
|
13257
|
-
void lightRulesEvaluatorJob.triggerIfNeeded(cron,
|
|
13513
|
+
onAfterIngest: (inserted, cron) => {
|
|
13514
|
+
void lightRulesEvaluatorJob.triggerIfNeeded(cron, inserted);
|
|
13258
13515
|
},
|
|
13259
13516
|
getCronForHttpIngest: () => cronService
|
|
13260
13517
|
});
|