@yoooclaw/phone-notifications 1.11.0-beta.2 → 1.11.0-beta.4

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 CHANGED
@@ -5422,7 +5422,7 @@ function readBuildInjectedVersion() {
5422
5422
  if (false) {
5423
5423
  return void 0;
5424
5424
  }
5425
- const version = "1.11.0-beta.2".trim();
5425
+ const version = "1.11.0-beta.4".trim();
5426
5426
  return version || void 0;
5427
5427
  }
5428
5428
  function readPluginVersionFromPackageJson() {
@@ -5989,112 +5989,6 @@ function isRecord(value) {
5989
5989
  // src/light-rules/storage.ts
5990
5990
  var import_node_fs4 = require("fs");
5991
5991
  var import_node_path3 = require("path");
5992
-
5993
- // src/monitor/fetch-gen.ts
5994
- function generateFetchPy(name, matchRules) {
5995
- const appName = typeof matchRules.appName === "string" ? matchRules.appName : "";
5996
- const senderKeywords = stringArray(matchRules.senderKeywords);
5997
- const contentKeywords = stringArray(matchRules.contentKeywords);
5998
- return `#!/usr/bin/env python3
5999
- """Auto-generated fetch script for monitor task: ${name}"""
6000
- import json, sys, os
6001
- from pathlib import Path
6002
-
6003
- def matches(notification: dict) -> bool:
6004
- app = str(notification.get("appName", "") or "")
6005
- title = str(notification.get("title", "") or "")
6006
- content = str(notification.get("content", "") or "")
6007
- body = str(notification.get("body", "") or "")
6008
-
6009
- app_name = ${pyLiteral(appName)}
6010
- if app_name and app != app_name:
6011
- return False
6012
-
6013
- sender_keywords = ${pyLiteral(senderKeywords)}
6014
- sender_haystack = f"{title}\\n{content}\\n{body}"
6015
- if sender_keywords and not any(keyword in sender_haystack for keyword in sender_keywords):
6016
- return False
6017
-
6018
- content_keywords = ${pyLiteral(contentKeywords)}
6019
- content_haystack = f"{content}\\n{body}"
6020
- if content_keywords and not any(keyword in content_haystack for keyword in content_keywords):
6021
- return False
6022
-
6023
- return True
6024
-
6025
- def main():
6026
- import argparse
6027
- parser = argparse.ArgumentParser()
6028
- parser.add_argument("--notifications-dir", required=True)
6029
- args = parser.parse_args()
6030
-
6031
- checkpoint_path = Path(__file__).parent / "checkpoint.json"
6032
- checkpoint = {}
6033
- if checkpoint_path.exists():
6034
- checkpoint = json.loads(checkpoint_path.read_text())
6035
-
6036
- ntf_dir = Path(args.notifications_dir)
6037
- matched = []
6038
- new_checkpoint = dict(checkpoint)
6039
-
6040
- for f in sorted(ntf_dir.glob("*.json")):
6041
- date_key = f.stem
6042
- items = json.loads(f.read_text())
6043
- last_idx = checkpoint.get(date_key, {}).get("lastIndex", -1)
6044
- for i, item in enumerate(items):
6045
- if i <= last_idx:
6046
- continue
6047
- if matches(item):
6048
- matched.append(item)
6049
- new_checkpoint[date_key] = {"lastIndex": len(items) - 1}
6050
-
6051
- checkpoint_path.write_text(json.dumps(new_checkpoint, indent=2))
6052
-
6053
- if not matched:
6054
- print("NO_MATCH")
6055
- return
6056
-
6057
- for m in matched:
6058
- print(json.dumps(m, ensure_ascii=False))
6059
-
6060
- if __name__ == "__main__":
6061
- main()
6062
- `;
6063
- }
6064
- function stringArray(value) {
6065
- if (!Array.isArray(value)) return [];
6066
- return value.filter((item) => typeof item === "string" && item.length > 0);
6067
- }
6068
- function pyLiteral(value) {
6069
- return JSON.stringify(value);
6070
- }
6071
-
6072
- // src/light-rules/storage.ts
6073
- var LEGACY_DEFAULT_CRON_SCHEDULE = "*/5 * * * *";
6074
- function legacyReadMatchRules(input) {
6075
- return input.matchRules ?? {};
6076
- }
6077
- function legacyReadCronSchedule(input) {
6078
- return input.cronSchedule ?? LEGACY_DEFAULT_CRON_SCHEDULE;
6079
- }
6080
- function legacyHasMatchRules(input) {
6081
- return input.matchRules !== void 0;
6082
- }
6083
- function legacyHasCronSchedule(input) {
6084
- return input.cronSchedule !== void 0;
6085
- }
6086
- function legacyAssignMatchRules(meta, matchRules) {
6087
- meta.matchRules = matchRules;
6088
- }
6089
- function legacyAssignCronSchedule(meta, cronSchedule) {
6090
- meta.cronSchedule = cronSchedule;
6091
- }
6092
- function legacyReadMetaMatchRules(meta) {
6093
- return meta.matchRules ?? {};
6094
- }
6095
- function legacyReadMetaCronSchedule(meta) {
6096
- return meta.cronSchedule;
6097
- }
6098
5992
  function resolveBaseDir(ctx) {
6099
5993
  if (ctx.workspaceDir) return ctx.workspaceDir;
6100
5994
  if (ctx.stateDir) {
@@ -6178,34 +6072,6 @@ function readMeta(taskDir) {
6178
6072
  function writeMeta(taskDir, meta) {
6179
6073
  (0, import_node_fs4.writeFileSync)((0, import_node_path3.join)(taskDir, "meta.json"), JSON.stringify(meta, null, 2), "utf-8");
6180
6074
  }
6181
- function generateLightRuleReadme(name, description, segments, repeatTimes) {
6182
- const segmentsJson = JSON.stringify(segments, null, 2);
6183
- return `# ${name}
6184
-
6185
- ## \u76D1\u63A7\u76EE\u6807
6186
-
6187
- ${description}
6188
-
6189
- ## \u89E6\u53D1\u540E\u5982\u4F55\u5904\u7406
6190
-
6191
- \u5F53\u4EFB\u52A1\u811A\u672C\u8F93\u51FA\u5339\u914D\u7ED3\u679C\u65F6\uFF1A
6192
-
6193
- 1. \u5C06\u8F93\u51FA\u6309"\u6BCF\u884C\u4E00\u4E2A JSON \u5BF9\u8C61"\u89E3\u6790
6194
- 2. \u8FD9\u4E9B\u662F\u5019\u9009\u901A\u77E5\uFF0C\u4E0D\u4EE3\u8868\u5DF2\u7ECF\u547D\u4E2D
6195
- 3. \u6839\u636E\u76D1\u63A7\u76EE\u6807\u5224\u65AD\u662F\u5426\u771F\u6B63\u547D\u4E2D
6196
-
6197
- **\u547D\u4E2D\u540E\u6267\u884C\u706F\u6548**\uFF1A\u8C03\u7528 light_control \u5DE5\u5177\uFF0C\u4F20\u5165\u4EE5\u4E0B\u706F\u6548\u53C2\u6570\uFF1A
6198
-
6199
- \`\`\`json
6200
- ${segmentsJson}
6201
- \`\`\`
6202
-
6203
- repeat_times: ${repeatTimes}
6204
- reason: "${description}"
6205
-
6206
- \u53EA\u6267\u884C\u706F\u6548\uFF0C\u4E0D\u4E3B\u52A8\u53D1\u9001\u901A\u77E5\u6458\u8981\u6216\u56DE\u590D\u7528\u6237\u3002
6207
- `;
6208
- }
6209
6075
  function listLightRules(ctx) {
6210
6076
  const dir = tasksDir(ctx);
6211
6077
  if (!(0, import_node_fs4.existsSync)(dir)) return [];
@@ -6229,8 +6095,6 @@ function createLightRule(ctx, params) {
6229
6095
  repeat_times: params.repeat_times
6230
6096
  });
6231
6097
  assertAncsRepeatTimes(repeatTimes);
6232
- const effectiveMatchRules = legacyReadMatchRules(params);
6233
- const effectiveCronSchedule = legacyReadCronSchedule(params);
6234
6098
  const meta = {
6235
6099
  name: params.name,
6236
6100
  type: "light-rule",
@@ -6240,34 +6104,8 @@ function createLightRule(ctx, params) {
6240
6104
  enabled: true,
6241
6105
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
6242
6106
  };
6243
- legacyAssignMatchRules(meta, effectiveMatchRules);
6244
- legacyAssignCronSchedule(meta, effectiveCronSchedule);
6245
6107
  writeMeta(taskDir, meta);
6246
- (0, import_node_fs4.writeFileSync)(
6247
- (0, import_node_path3.join)(taskDir, "fetch.py"),
6248
- generateFetchPy(params.name, effectiveMatchRules),
6249
- "utf-8"
6250
- );
6251
- (0, import_node_fs4.writeFileSync)(
6252
- (0, import_node_path3.join)(taskDir, "README.md"),
6253
- generateLightRuleReadme(params.name, params.description, params.segments, repeatTimes),
6254
- "utf-8"
6255
- );
6256
- return {
6257
- meta,
6258
- cronHint: {
6259
- action: "add",
6260
- job: {
6261
- name: `notif-${params.name}`,
6262
- schedule: effectiveCronSchedule,
6263
- sessionTarget: "isolated",
6264
- message: `\u624B\u673A\u901A\u77E5\u5DF2\u7531\u72EC\u7ACB\u670D\u52A1\u5B9E\u65F6\u6355\u83B7\u5230 notifications/ \u76EE\u5F55\u7684 JSON \u6587\u4EF6\u4E2D\u3002
6265
- \u6267\u884C\uFF1Apython3 tasks/${params.name}/fetch.py --notifications-dir notifications
6266
- - NO_CHANGE \u6216 NO_MATCH \u2192 \u4E0D\u56DE\u590D\uFF0C\u76F4\u63A5\u7ED3\u675F\u3002
6267
- - \u6709\u8F93\u51FA \u2192 \u8BFB tasks/${params.name}/README.md \u4E86\u89E3\u5982\u4F55\u5904\u7406\u6570\u636E\u5E76\u901A\u77E5\u7528\u6237\u3002`
6268
- }
6269
- }
6270
- };
6108
+ return { meta };
6271
6109
  }
6272
6110
  function updateLightRule(ctx, params) {
6273
6111
  const resolved = resolveLightRuleTask(ctx, params.name);
@@ -6276,19 +6114,11 @@ function updateLightRule(ctx, params) {
6276
6114
  if (!taskDir || !meta) {
6277
6115
  throw new LightRuleError("NOT_FOUND", `\u706F\u6548\u89C4\u5219 '${params.name}' \u4E0D\u5B58\u5728`);
6278
6116
  }
6279
- let regenerateFetch = false;
6280
- let regenerateReadme = false;
6281
6117
  if (params.description !== void 0) {
6282
6118
  meta.description = params.description;
6283
- regenerateReadme = true;
6284
- }
6285
- if (legacyHasMatchRules(params)) {
6286
- legacyAssignMatchRules(meta, legacyReadMatchRules(params));
6287
- regenerateFetch = true;
6288
6119
  }
6289
6120
  if (params.segments !== void 0) {
6290
6121
  meta.segments = params.segments;
6291
- regenerateReadme = true;
6292
6122
  }
6293
6123
  if (params.repeat !== void 0 || params.repeat_times !== void 0) {
6294
6124
  meta.repeat_times = normalizeRepeatTimes({
@@ -6296,38 +6126,13 @@ function updateLightRule(ctx, params) {
6296
6126
  repeat_times: params.repeat_times
6297
6127
  });
6298
6128
  assertAncsRepeatTimes(meta.repeat_times);
6299
- regenerateReadme = true;
6300
- }
6301
- if (legacyHasCronSchedule(params)) {
6302
- legacyAssignCronSchedule(meta, legacyReadCronSchedule(params));
6303
6129
  }
6304
6130
  if (params.enabled !== void 0) {
6305
6131
  meta.enabled = params.enabled;
6306
6132
  }
6307
6133
  meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
6308
6134
  writeMeta(taskDir, meta);
6309
- if (regenerateFetch) {
6310
- (0, import_node_fs4.writeFileSync)(
6311
- (0, import_node_path3.join)(taskDir, "fetch.py"),
6312
- generateFetchPy(meta.name, legacyReadMetaMatchRules(meta)),
6313
- "utf-8"
6314
- );
6315
- }
6316
- if (regenerateReadme) {
6317
- (0, import_node_fs4.writeFileSync)(
6318
- (0, import_node_path3.join)(taskDir, "README.md"),
6319
- generateLightRuleReadme(meta.name, meta.description, meta.segments, meta.repeat_times),
6320
- "utf-8"
6321
- );
6322
- }
6323
- const cronHint = legacyHasCronSchedule(params) ? {
6324
- action: "update",
6325
- job: {
6326
- name: `notif-${meta.name}`,
6327
- schedule: legacyReadMetaCronSchedule(meta)
6328
- }
6329
- } : void 0;
6330
- return { meta, cronHint };
6135
+ return { meta };
6331
6136
  }
6332
6137
  function deleteLightRule(ctx, name) {
6333
6138
  const resolved = resolveLightRuleTask(ctx, name);
@@ -6337,13 +6142,7 @@ function deleteLightRule(ctx, name) {
6337
6142
  throw new LightRuleError("NOT_FOUND", `\u706F\u6548\u89C4\u5219 '${name}' \u4E0D\u5B58\u5728`);
6338
6143
  }
6339
6144
  (0, import_node_fs4.rmSync)(taskDir, { recursive: true, force: true });
6340
- return {
6341
- name: meta.name,
6342
- cronHint: {
6343
- action: "remove",
6344
- name: `notif-${meta.name}`
6345
- }
6346
- };
6145
+ return { name: meta.name };
6347
6146
  }
6348
6147
  var LightRuleError = class extends Error {
6349
6148
  constructor(code, message) {
@@ -6354,7 +6153,6 @@ var LightRuleError = class extends Error {
6354
6153
  };
6355
6154
 
6356
6155
  // src/light-rules/gateway.ts
6357
- var DEFAULT_CRON_SCHEDULE = "*/5 * * * *";
6358
6156
  function resolveRuleIdentifier(params) {
6359
6157
  if (!params || typeof params !== "object") return void 0;
6360
6158
  const raw = params;
@@ -6390,7 +6188,7 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6390
6188
  }
6391
6189
  });
6392
6190
  registerGatewayMethodWithBroadcastCapture("lightrules.create", async ({ params, respond }) => {
6393
- const { name, description, matchRules, segments, repeat, repeat_times, cronSchedule } = params;
6191
+ const { name, description, segments, repeat, repeat_times } = params;
6394
6192
  if (!name || typeof name !== "string") {
6395
6193
  respond(false, null, { code: "INVALID_PARAMS", message: "name is required" });
6396
6194
  return;
@@ -6399,10 +6197,6 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6399
6197
  respond(false, null, { code: "INVALID_PARAMS", message: "description is required" });
6400
6198
  return;
6401
6199
  }
6402
- if (!matchRules || typeof matchRules !== "object") {
6403
- respond(false, null, { code: "INVALID_PARAMS", message: "matchRules is required" });
6404
- return;
6405
- }
6406
6200
  const validation = validateSegments(segments);
6407
6201
  if (!validation.valid) {
6408
6202
  respond(false, null, {
@@ -6423,13 +6217,11 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6423
6217
  const result = await registry.create({
6424
6218
  name,
6425
6219
  description,
6426
- matchRules,
6427
6220
  segments: validation.segments,
6428
- repeat_times: repeatTimes,
6429
- cronSchedule: cronSchedule || DEFAULT_CRON_SCHEDULE
6221
+ repeat_times: repeatTimes
6430
6222
  });
6431
6223
  logger.info(`Light rule created: ${name}`);
6432
- respond(true, { ok: true, name, cronHint: result.cronHint });
6224
+ respond(true, { ok: true, name, rule: result.meta });
6433
6225
  } catch (err2) {
6434
6226
  if (err2 instanceof LightRuleError) {
6435
6227
  respond(false, null, { code: err2.code, message: err2.message });
@@ -6440,15 +6232,7 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6440
6232
  }
6441
6233
  });
6442
6234
  registerGatewayMethodWithBroadcastCapture("lightrules.update", async ({ params, respond }) => {
6443
- const {
6444
- description,
6445
- matchRules,
6446
- segments,
6447
- repeat,
6448
- repeat_times,
6449
- cronSchedule,
6450
- enabled
6451
- } = params;
6235
+ const { description, segments, repeat, repeat_times, enabled } = params;
6452
6236
  const name = resolveRuleIdentifier(params);
6453
6237
  if (!name) {
6454
6238
  respond(false, null, {
@@ -6483,10 +6267,8 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6483
6267
  const result = await registry.update({
6484
6268
  name,
6485
6269
  description,
6486
- matchRules,
6487
6270
  segments: validatedSegments,
6488
6271
  repeat_times: repeatTimes,
6489
- cronSchedule,
6490
6272
  enabled
6491
6273
  });
6492
6274
  logger.info(`Light rule updated: ${name}`);
@@ -6495,8 +6277,7 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6495
6277
  id: result.meta.name,
6496
6278
  name: result.meta.name,
6497
6279
  updated: true,
6498
- rule: result.meta,
6499
- cronHint: result.cronHint
6280
+ rule: result.meta
6500
6281
  });
6501
6282
  } catch (err2) {
6502
6283
  if (err2 instanceof LightRuleError) {
@@ -6523,8 +6304,7 @@ function registerLightRulesGateway(api, registry, logger, rememberBroadcast) {
6523
6304
  ok: true,
6524
6305
  id: result.name,
6525
6306
  name: result.name,
6526
- deleted: true,
6527
- cronHint: result.cronHint
6307
+ deleted: true
6528
6308
  });
6529
6309
  } catch (err2) {
6530
6310
  if (err2 instanceof LightRuleError) {
@@ -6960,7 +6740,7 @@ function registerLightRulesTools(api, registry, logger) {
6960
6740
  registerToolWithAliases(api, {
6961
6741
  name: "lightrules.create",
6962
6742
  label: "Create Light Rule",
6963
- description: '\u521B\u5EFA\u4E00\u6761\u706F\u6548\u89C4\u5219\uFF0C\u6307\u5B9A\u540D\u79F0\u3001\u81EA\u7136\u8BED\u8A00\u89E6\u53D1\u63CF\u8FF0\u548C\u706F\u6548\u53C2\u6570\u3002\u5F53\u7528\u6237\u8BF4"\u521B\u5EFA\u706F\u6548\u89C4\u5219"\u3001"\u65B0\u589E\u89C4\u5219"\u7B49\u65F6\u8C03\u7528\u3002',
6743
+ description: '\u521B\u5EFA\u4E00\u6761\u6301\u4E45\u706F\u6548\u89C4\u5219\uFF0C\u6307\u5B9A\u540D\u79F0\u3001\u81EA\u7136\u8BED\u8A00\u89E6\u53D1\u63CF\u8FF0\u548C\u706F\u6548\u53C2\u6570\u3002\u89C4\u5219\u4F1A\u4FDD\u5B58\u5230 tasks/<name>/meta.json\uFF0C\u5E76\u7531\u901A\u77E5\u5230\u8FBE\u540E\u7684\u4E8B\u4EF6\u9A71\u52A8\u8BC4\u4F30\u89E6\u53D1\u3002\u5F53\u7528\u6237\u8BF4"\u6536\u5230\u67D0\u7C7B\u901A\u77E5/\u6D88\u606F\u65F6\u4EAE\u706F/\u95EA\u706F/\u53D8\u706F\u6548"\u3001"\u5F53\u8001\u677F\u53D1\u6D88\u606F\u4EAE\u7EA2\u706F"\u3001"\u65B0\u589E/\u521B\u5EFA\u706F\u6548\u89C4\u5219"\u7B49\u65F6\u8C03\u7528\u3002\u4E0D\u8981\u76F4\u63A5\u8C03\u7528 light_control \u4EE3\u66FF\u521B\u5EFA\u89C4\u5219\uFF1Blight_control \u53EA\u4F1A\u7ACB\u5373\u4EAE\u4E00\u6B21\u706F\uFF0C\u4E0D\u4F1A\u51FA\u73B0\u5728\u706F\u6548\u89C4\u5219\u67E5\u8BE2\u4E2D\u3002',
6964
6744
  parameters: {
6965
6745
  type: "object",
6966
6746
  required: ["name", "description", "segments"],
@@ -6997,13 +6777,11 @@ function registerLightRulesTools(api, registry, logger) {
6997
6777
  const result = await registry.create({
6998
6778
  name,
6999
6779
  description,
7000
- matchRules: {},
7001
6780
  segments: validation.segments,
7002
- repeat_times: repeatTimes,
7003
- cronSchedule: "*/5 * * * *"
6781
+ repeat_times: repeatTimes
7004
6782
  });
7005
6783
  logger.info(`lightrules.create tool: created ${name}`);
7006
- return ok({ ok: true, name, cronHint: result.cronHint });
6784
+ return ok({ ok: true, name, rule: result.meta });
7007
6785
  } catch (e) {
7008
6786
  if (e instanceof LightRuleError) return err(e.code, e.message);
7009
6787
  logger.warn(`lightrules.create tool failed: ${e?.message}`);
@@ -7059,8 +6837,7 @@ function registerLightRulesTools(api, registry, logger) {
7059
6837
  ok: true,
7060
6838
  name: result.meta.name,
7061
6839
  updated: true,
7062
- rule: result.meta,
7063
- cronHint: result.cronHint
6840
+ rule: result.meta
7064
6841
  });
7065
6842
  } catch (e) {
7066
6843
  if (e instanceof LightRuleError) return err(e.code, e.message);
@@ -7088,7 +6865,7 @@ function registerLightRulesTools(api, registry, logger) {
7088
6865
  try {
7089
6866
  const result = await registry.delete(name);
7090
6867
  logger.info(`lightrules.delete tool: deleted ${name}`);
7091
- return ok({ ok: true, name: result.name, deleted: true, cronHint: result.cronHint });
6868
+ return ok({ ok: true, name: result.name, deleted: true });
7092
6869
  } catch (e) {
7093
6870
  if (e instanceof LightRuleError) return err(e.code, e.message);
7094
6871
  logger.warn(`lightrules.delete tool failed: ${e?.message}`);
@@ -7273,15 +7050,32 @@ function migrateTaskDir(taskDir, logger) {
7273
7050
  }
7274
7051
  if (meta.type !== "light-rule") return;
7275
7052
  const name = typeof meta.name === "string" ? meta.name : taskDir;
7276
- mergeMatchRulesIntoDescription(meta, name, metaPath, logger);
7053
+ cleanupLegacyMetaFields(meta, name, metaPath, logger);
7277
7054
  replaceFetchPy(taskDir, name, logger);
7278
7055
  for (const filename of ["README.md", "checkpoint.json"]) {
7279
7056
  removeFile((0, import_node_path7.join)(taskDir, filename), name, filename, logger);
7280
7057
  }
7281
7058
  }
7282
- function mergeMatchRulesIntoDescription(meta, name, metaPath, logger) {
7059
+ function cleanupLegacyMetaFields(meta, name, metaPath, logger) {
7060
+ const cleaned = [];
7061
+ if (mergeMatchRulesIntoDescription(meta)) cleaned.push("matchRules");
7062
+ if (meta.cronSchedule !== void 0) {
7063
+ delete meta.cronSchedule;
7064
+ cleaned.push("cronSchedule");
7065
+ }
7066
+ if (cleaned.length === 0) return;
7067
+ try {
7068
+ (0, import_node_fs8.writeFileSync)(metaPath, JSON.stringify(meta, null, 2), "utf-8");
7069
+ logger.info(
7070
+ `migration: cleaned deprecated field(s) [${cleaned.join(", ")}] in meta.json for light rule: ${name}`
7071
+ );
7072
+ } catch (err2) {
7073
+ logger.warn(`migration: failed to update meta.json for ${name}: ${err2?.message}`);
7074
+ }
7075
+ }
7076
+ function mergeMatchRulesIntoDescription(meta) {
7283
7077
  const matchRules = meta.matchRules;
7284
- if (!matchRules || typeof matchRules !== "object") return;
7078
+ if (!matchRules || typeof matchRules !== "object") return false;
7285
7079
  const rules = matchRules;
7286
7080
  const parts = [];
7287
7081
  if (rules.appName) parts.push(`app=${rules.appName}`);
@@ -7296,12 +7090,7 @@ function mergeMatchRulesIntoDescription(meta, name, metaPath, logger) {
7296
7090
  meta.description = existing ? `${existing}\u3002\u5339\u914D\u89C4\u5219\uFF1A${parts.join("\uFF0C")}` : `\u5339\u914D\u89C4\u5219\uFF1A${parts.join("\uFF0C")}`;
7297
7091
  }
7298
7092
  delete meta.matchRules;
7299
- try {
7300
- (0, import_node_fs8.writeFileSync)(metaPath, JSON.stringify(meta, null, 2), "utf-8");
7301
- logger.info(`migration: merged matchRules into description for light rule: ${name}`);
7302
- } catch (err2) {
7303
- logger.warn(`migration: failed to update meta.json for ${name}: ${err2?.message}`);
7304
- }
7093
+ return true;
7305
7094
  }
7306
7095
  function replaceFetchPy(taskDir, name, logger) {
7307
7096
  const fetchPyPath = (0, import_node_path7.join)(taskDir, "fetch.py");
@@ -8153,6 +7942,87 @@ function registerNtfSync(ntf, ctx) {
8153
7942
  // src/cli/ntf-monitor.ts
8154
7943
  var import_node_fs12 = require("fs");
8155
7944
  var import_node_path11 = require("path");
7945
+
7946
+ // src/monitor/fetch-gen.ts
7947
+ function generateFetchPy(name, matchRules) {
7948
+ const appName = typeof matchRules.appName === "string" ? matchRules.appName : "";
7949
+ const senderKeywords = stringArray(matchRules.senderKeywords);
7950
+ const contentKeywords = stringArray(matchRules.contentKeywords);
7951
+ return `#!/usr/bin/env python3
7952
+ """Auto-generated fetch script for monitor task: ${name}"""
7953
+ import json, sys, os
7954
+ from pathlib import Path
7955
+
7956
+ def matches(notification: dict) -> bool:
7957
+ app = str(notification.get("appName", "") or "")
7958
+ title = str(notification.get("title", "") or "")
7959
+ content = str(notification.get("content", "") or "")
7960
+ body = str(notification.get("body", "") or "")
7961
+
7962
+ app_name = ${pyLiteral(appName)}
7963
+ if app_name and app != app_name:
7964
+ return False
7965
+
7966
+ sender_keywords = ${pyLiteral(senderKeywords)}
7967
+ sender_haystack = f"{title}\\n{content}\\n{body}"
7968
+ if sender_keywords and not any(keyword in sender_haystack for keyword in sender_keywords):
7969
+ return False
7970
+
7971
+ content_keywords = ${pyLiteral(contentKeywords)}
7972
+ content_haystack = f"{content}\\n{body}"
7973
+ if content_keywords and not any(keyword in content_haystack for keyword in content_keywords):
7974
+ return False
7975
+
7976
+ return True
7977
+
7978
+ def main():
7979
+ import argparse
7980
+ parser = argparse.ArgumentParser()
7981
+ parser.add_argument("--notifications-dir", required=True)
7982
+ args = parser.parse_args()
7983
+
7984
+ checkpoint_path = Path(__file__).parent / "checkpoint.json"
7985
+ checkpoint = {}
7986
+ if checkpoint_path.exists():
7987
+ checkpoint = json.loads(checkpoint_path.read_text())
7988
+
7989
+ ntf_dir = Path(args.notifications_dir)
7990
+ matched = []
7991
+ new_checkpoint = dict(checkpoint)
7992
+
7993
+ for f in sorted(ntf_dir.glob("*.json")):
7994
+ date_key = f.stem
7995
+ items = json.loads(f.read_text())
7996
+ last_idx = checkpoint.get(date_key, {}).get("lastIndex", -1)
7997
+ for i, item in enumerate(items):
7998
+ if i <= last_idx:
7999
+ continue
8000
+ if matches(item):
8001
+ matched.append(item)
8002
+ new_checkpoint[date_key] = {"lastIndex": len(items) - 1}
8003
+
8004
+ checkpoint_path.write_text(json.dumps(new_checkpoint, indent=2))
8005
+
8006
+ if not matched:
8007
+ print("NO_MATCH")
8008
+ return
8009
+
8010
+ for m in matched:
8011
+ print(json.dumps(m, ensure_ascii=False))
8012
+
8013
+ if __name__ == "__main__":
8014
+ main()
8015
+ `;
8016
+ }
8017
+ function stringArray(value) {
8018
+ if (!Array.isArray(value)) return [];
8019
+ return value.filter((item) => typeof item === "string" && item.length > 0);
8020
+ }
8021
+ function pyLiteral(value) {
8022
+ return JSON.stringify(value);
8023
+ }
8024
+
8025
+ // src/cli/ntf-monitor.ts
8156
8026
  function tasksDir2(ctx) {
8157
8027
  const base = ctx.workspaceDir || ctx.stateDir;
8158
8028
  if (!base) throw new Error("workspaceDir and stateDir both unavailable");
@@ -9538,7 +9408,7 @@ function registerLightControlTool(api, logger) {
9538
9408
  api.registerTool({
9539
9409
  name: "light_control",
9540
9410
  label: "Light Control",
9541
- description: '\u63A7\u5236\u786C\u4EF6\u706F\u6548\uFF0C\u652F\u6301 1\u201312 \u6BB5\u987A\u5E8F\u706F\u6548\uFF0C\u6BCF\u6BB5\u72EC\u7ACB\u53C2\u6570\uFF086 \u79CD\u6A21\u5F0F\uFF1A\u6CE2\u6D6A/\u547C\u5438/\u9891\u95EA/\u5E38\u4EAE/\u6D41\u5149/\u9010\u7EC4\u50CF\u7D20\u5E27\uFF09\u3002Tool \u5185\u90E8\u81EA\u52A8\u91CF\u5316\u4E3A\u5D4C\u5165\u5F0F\u534F\u8BAE\u5B9A\u4E49\u7684\u79BB\u6563\u6863\u4F4D\uFF0C\u5E76\u6309\u6A21\u5F0F\u7CBE\u7B80\u4E3A\u53D8\u957F ANCS \u63A7\u5236\u5E8F\u5217\uFF0C\u901A\u8FC7 HTTP \u63A5\u53E3\u4E0B\u53D1\u706F\u6548\u6307\u4EE4\u3002\u5F53\u7528\u6237\u8BF4"\u5F00\u706F"\u3001"\u6362\u4E2A\u706F\u6548"\u3001"\u628A\u706F\u8C03\u6210\u7EA2\u8272"\u3001"\u8425\u9020\u6C1B\u56F4"\u7B49\u4E0E\u706F\u5149\u63A7\u5236\u76F8\u5173\u7684\u8868\u8FBE\u65F6\u8C03\u7528\u3002',
9411
+ description: '\u7ACB\u5373\u63A7\u5236\u786C\u4EF6\u706F\u6548\uFF08\u4E00\u6B21\u6027\u6267\u884C\uFF0C\u4E0D\u4F1A\u521B\u5EFA\u6216\u4FDD\u5B58\u901A\u77E5\u89E6\u53D1\u89C4\u5219\uFF09\uFF0C\u652F\u6301 1\u201312 \u6BB5\u987A\u5E8F\u706F\u6548\uFF0C\u6BCF\u6BB5\u72EC\u7ACB\u53C2\u6570\uFF086 \u79CD\u6A21\u5F0F\uFF1A\u6CE2\u6D6A/\u547C\u5438/\u9891\u95EA/\u5E38\u4EAE/\u6D41\u5149/\u9010\u7EC4\u50CF\u7D20\u5E27\uFF09\u3002Tool \u5185\u90E8\u81EA\u52A8\u91CF\u5316\u4E3A\u5D4C\u5165\u5F0F\u534F\u8BAE\u5B9A\u4E49\u7684\u79BB\u6563\u6863\u4F4D\uFF0C\u5E76\u6309\u6A21\u5F0F\u7CBE\u7B80\u4E3A\u53D8\u957F ANCS \u63A7\u5236\u5E8F\u5217\uFF0C\u901A\u8FC7 HTTP \u63A5\u53E3\u4E0B\u53D1\u706F\u6548\u6307\u4EE4\u3002\u5F53\u7528\u6237\u8BF4"\u5F00\u706F"\u3001"\u6362\u4E2A\u706F\u6548"\u3001"\u628A\u706F\u8C03\u6210\u7EA2\u8272"\u3001"\u8425\u9020\u6C1B\u56F4"\u3001"\u6D4B\u8BD5/\u9884\u89C8\u706F\u6548"\u7B49\u4E0E\u5F53\u524D\u5373\u65F6\u706F\u5149\u63A7\u5236\u76F8\u5173\u7684\u8868\u8FBE\u65F6\u8C03\u7528\u3002\u5982\u679C\u7528\u6237\u8BF4"\u6536\u5230\u67D0\u7C7B\u901A\u77E5/\u6D88\u606F\u65F6\u4EAE\u706F/\u95EA\u706F/\u53D8\u706F\u6548"\uFF0C\u90A3\u662F\u6301\u4E45\u706F\u6548\u89C4\u5219\uFF0C\u6539\u7528 lightrules.create \u6216 lightrules.update\u3002',
9542
9412
  parameters: lightControlParameters,
9543
9413
  async execute(_toolCallId, params) {
9544
9414
  let apiKey;
@@ -13205,6 +13075,12 @@ function isPluginCliInvocation(argv = process.argv) {
13205
13075
  }
13206
13076
 
13207
13077
  // src/index.ts
13078
+ var LIGHT_RULE_PROMPT_GUIDANCE = [
13079
+ "\u706F\u6548\u89C4\u5219\u8DEF\u7531\u63D0\u793A\uFF1A",
13080
+ "- \u5F53\u7528\u6237\u8868\u8FBE\u201C\u6536\u5230/\u5F53/\u5982\u679C/\u6709\u67D0\u7C7B\u901A\u77E5\u6216\u6D88\u606F\u65F6\uFF0C\u4EAE\u706F/\u95EA\u706F/\u53D8\u6210\u67D0\u79CD\u706F\u6548\u201D\u65F6\uFF0C\u8FD9\u662F\u6301\u4E45\u706F\u6548\u89C4\u5219\u8BF7\u6C42\uFF0C\u5FC5\u987B\u8C03\u7528 lightrules.create \u6216 lightrules.update\u3002",
13081
+ "- \u4E0D\u8981\u4E3A\u8FD9\u7C7B\u901A\u77E5\u89E6\u53D1\u89C4\u5219\u76F4\u63A5\u8C03\u7528 light_control\uFF1Blight_control \u53EA\u7528\u4E8E\u5F53\u524D\u7ACB\u5373\u6267\u884C\u7684\u4E00\u6B21\u6027\u706F\u6548\u3001\u6D4B\u8BD5\u6216\u9884\u89C8\u3002",
13082
+ "- \u4E0D\u8981\u624B\u5199\u6216\u7F16\u8F91 tasks/*/meta.json\uFF1B\u706F\u6548\u89C4\u5219\u7684\u521B\u5EFA\u3001\u4FEE\u6539\u3001\u5220\u9664\u548C\u67E5\u8BE2\u90FD\u8D70 lightrules.* \u5DE5\u5177\u3002"
13083
+ ].join("\n");
13208
13084
  var index_default = {
13209
13085
  id: "phone-notifications",
13210
13086
  name: "Phone Notifications & Light Control",
@@ -13232,6 +13108,9 @@ var index_default = {
13232
13108
  );
13233
13109
  return;
13234
13110
  }
13111
+ api.on("before_prompt_build", () => ({
13112
+ appendSystemContext: LIGHT_RULE_PROMPT_GUIDANCE
13113
+ }));
13235
13114
  function cacheBroadcast(broadcast) {
13236
13115
  if (!broadcast) {
13237
13116
  return;