@yoooclaw/phone-notifications 1.12.2 → 1.12.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.
Files changed (75) hide show
  1. package/README.md +3 -4
  2. package/dist/bin/ntf.cjs +327 -12
  3. package/dist/bin/ntf.cjs.map +9 -7
  4. package/dist/cli/doctor/check-local-gateway.d.ts +3 -0
  5. package/dist/cli/doctor/check-local-gateway.d.ts.map +1 -0
  6. package/dist/cli/doctor/checkers.d.ts.map +1 -1
  7. package/dist/env.d.ts +16 -1
  8. package/dist/env.d.ts.map +1 -1
  9. package/dist/fs-atomic.d.ts +11 -0
  10. package/dist/fs-atomic.d.ts.map +1 -0
  11. package/dist/image/store.d.ts.map +1 -1
  12. package/dist/index.cjs +2444 -1911
  13. package/dist/index.cjs.map +38 -31
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/light/sender.d.ts +14 -3
  16. package/dist/light/sender.d.ts.map +1 -1
  17. package/dist/logger.d.ts.map +1 -1
  18. package/dist/notification/storage.d.ts +4 -3
  19. package/dist/notification/storage.d.ts.map +1 -1
  20. package/dist/plugin/auto-update.d.ts +2 -0
  21. package/dist/plugin/auto-update.d.ts.map +1 -1
  22. package/dist/plugin/lifecycle.d.ts +7 -14
  23. package/dist/plugin/lifecycle.d.ts.map +1 -1
  24. package/dist/plugin/light-control.d.ts.map +1 -1
  25. package/dist/plugin/light-rules-tools.d.ts.map +1 -1
  26. package/dist/plugin/notifications.d.ts +1 -0
  27. package/dist/plugin/notifications.d.ts.map +1 -1
  28. package/dist/plugin/recordings.d.ts +0 -2
  29. package/dist/plugin/recordings.d.ts.map +1 -1
  30. package/dist/plugin/register-guard.d.ts +16 -0
  31. package/dist/plugin/register-guard.d.ts.map +1 -0
  32. package/dist/plugin/runtime.d.ts +64 -0
  33. package/dist/plugin/runtime.d.ts.map +1 -0
  34. package/dist/plugin/shared-storage.d.ts +31 -0
  35. package/dist/plugin/shared-storage.d.ts.map +1 -0
  36. package/dist/plugin/system.d.ts +17 -0
  37. package/dist/plugin/system.d.ts.map +1 -0
  38. package/dist/recording/downloader.d.ts +0 -7
  39. package/dist/recording/downloader.d.ts.map +1 -1
  40. package/dist/recording/index.d.ts +2 -4
  41. package/dist/recording/index.d.ts.map +1 -1
  42. package/dist/recording/result-writer.d.ts +1 -2
  43. package/dist/recording/result-writer.d.ts.map +1 -1
  44. package/dist/recording/storage.d.ts +3 -2
  45. package/dist/recording/storage.d.ts.map +1 -1
  46. package/dist/system/os-info.d.ts +34 -0
  47. package/dist/system/os-info.d.ts.map +1 -0
  48. package/dist/tunnel/connect-status-reporter.d.ts.map +1 -1
  49. package/dist/tunnel/frame-slimmer.d.ts +97 -2
  50. package/dist/tunnel/frame-slimmer.d.ts.map +1 -1
  51. package/dist/tunnel/gateway-target.d.ts +17 -0
  52. package/dist/tunnel/gateway-target.d.ts.map +1 -0
  53. package/dist/tunnel/http-proxy.d.ts.map +1 -1
  54. package/dist/tunnel/proxy.d.ts +20 -1
  55. package/dist/tunnel/proxy.d.ts.map +1 -1
  56. package/dist/tunnel/relay-client.d.ts.map +1 -1
  57. package/dist/tunnel/service.d.ts.map +1 -1
  58. package/dist/types.d.ts +12 -67
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/update/executor.d.ts +3 -3
  61. package/dist/update/executor.d.ts.map +1 -1
  62. package/dist/update/index.d.ts +11 -0
  63. package/dist/update/index.d.ts.map +1 -1
  64. package/dist/update/stale-runtime.d.ts +43 -0
  65. package/dist/update/stale-runtime.d.ts.map +1 -0
  66. package/openclaw.plugin.json +1 -0
  67. package/package.json +1 -1
  68. package/skills/notification-monitor/SKILL.md +2 -2
  69. package/skills/notification-to-memory/SKILL.md +2 -2
  70. package/dist/recording/asr.d.ts +0 -119
  71. package/dist/recording/asr.d.ts.map +0 -1
  72. package/dist/recording/handler.d.ts +0 -38
  73. package/dist/recording/handler.d.ts.map +0 -1
  74. package/dist/recording/whisper-local.d.ts +0 -81
  75. package/dist/recording/whisper-local.d.ts.map +0 -1
package/README.md CHANGED
@@ -196,10 +196,9 @@ bash install.sh --tgz-url ./yoooclaw-phone-notifications-1.2.3.tgz
196
196
  4. 备份旧版本(如有),原子替换到 `<stateDir>/extensions/phone-notifications`
197
197
  5. 写入 `<configPath>` 配置:
198
198
  - `plugins.entries` — 启用插件,合并用户配置
199
- - `plugins.installs` — 记录安装来源和版本
200
199
  - `tools.allow` / `tools.alsoAllow` 与 `agents.main.tools.*` — 根据插件 `contracts.tools` 自动放行工具
201
200
  - 若宿主已启用 `plugins.allow` 白名单,会自动补入 `phone-notifications`,确保插件能被宿主加载
202
- - 清理该插件旧的 `plugins.load.paths` / 旧安装记录,避免继续命中之前通过 `openclaw plugins install` 或 `--link` 保留的副本
201
+ - 清理该插件旧的 `plugins.load.paths` / 旧的 `plugins.installs` 安装记录(该字段已被宿主废弃,插件不再读写),避免继续命中之前通过 `openclaw plugins install` 或 `--link` 保留的副本
203
202
  6. 如传入 `--api-key` / `--token`,写入 `<stateDir>/credentials.json`
204
203
  7. OpenClaw 下尝试执行 `gateway restart`,并验证 `ntf` / `phone-notifications` 命令是否可用;JvsClaw 下触发 `gateway restart` 后跳过即时 CLI 自检,避免平台 RPC 恢复期间误报;QClaw 下不会主动重启宿主进程
205
204
  8. 安装失败时自动回滚到旧版本
@@ -663,10 +662,10 @@ openclaw ntf monitor delete boss-alert --yes
663
662
 
664
663
  ```bash
665
664
 
666
- # 启用云端灯效规则工具(lightrules_list/create/update/delete)
665
+ # 启用一次性灯效及云端灯效规则工具
667
666
  openclaw ntf light setup
668
667
 
669
- # 调试遗留命令:直接发送灯效指令到硬件设备(OpenClaw 对话不再注册 light_control)
668
+ # 调试命令:直接发送灯效指令到硬件设备
670
669
  openclaw ntf light send \
671
670
  --segments '[{"mode":"wave","duration_s":4,"brightness":192,"color":{"r":255,"g":0,"b":0}}]'
672
671
 
package/dist/bin/ntf.cjs CHANGED
@@ -4704,6 +4704,7 @@ var DEFAULT_ENV_HOSTS = {
4704
4704
  production: "openclaw-service.yoooclaw.com"
4705
4705
  };
4706
4706
  var NOTIFICATION_INTELLIGENCE_LIGHT_RULES_PLUGIN_PATH = "/api/plugin/notification-intelligence/light-rules";
4707
+ var NOTIFICATION_INTELLIGENCE_LIGHT_EFFECTS_SEND_PLUGIN_PATH = "/api/plugin/notification-intelligence/light-effects/send";
4707
4708
  var NOTIFICATION_INTELLIGENCE_LIGHT_RULES_APP_PATH = "/api/notification-intelligence/light-rules";
4708
4709
  var NOTIFICATION_INTELLIGENCE_PLUGIN_BASE_PATH = "/api/plugin/notification-intelligence";
4709
4710
  var NOTIFICATION_INTELLIGENCE_APP_BASE_PATH = "/api/notification-intelligence";
@@ -4751,13 +4752,29 @@ function normalizeNotificationIntelligenceLightRulesPluginUrl(url) {
4751
4752
  }
4752
4753
  return trimmed;
4753
4754
  }
4755
+ function normalizeNotificationIntelligenceLightEffectsSendUrl(url) {
4756
+ const trimmed = url.trim().replace(/\/+$/, "");
4757
+ if (!trimmed)
4758
+ return trimmed;
4759
+ if (trimmed.endsWith(NOTIFICATION_INTELLIGENCE_LIGHT_EFFECTS_SEND_PLUGIN_PATH)) {
4760
+ return trimmed;
4761
+ }
4762
+ const normalizedRulesUrl = normalizeNotificationIntelligenceLightRulesPluginUrl(trimmed);
4763
+ if (normalizedRulesUrl.endsWith(NOTIFICATION_INTELLIGENCE_LIGHT_RULES_PLUGIN_PATH)) {
4764
+ const origin = normalizedRulesUrl.slice(0, -NOTIFICATION_INTELLIGENCE_LIGHT_RULES_PLUGIN_PATH.length);
4765
+ return `${origin}${NOTIFICATION_INTELLIGENCE_LIGHT_EFFECTS_SEND_PLUGIN_PATH}`;
4766
+ }
4767
+ return `${trimmed}/light-effects/send`;
4768
+ }
4754
4769
  function buildEnvUrls(host) {
4755
4770
  const https = `https://${host}`;
4756
4771
  const wss = `wss://${host}`;
4757
4772
  const lightRulesUrl = normalizeNotificationIntelligenceLightRulesPluginUrl(process.env.NOTIFICATION_INTELLIGENCE_LIGHT_RULES_URL || `${https}${NOTIFICATION_INTELLIGENCE_LIGHT_RULES_PLUGIN_PATH}`);
4773
+ const lightEffectsSendUrl = normalizeNotificationIntelligenceLightEffectsSendUrl(process.env.NOTIFICATION_INTELLIGENCE_LIGHT_EFFECTS_SEND_URL || lightRulesUrl);
4758
4774
  return {
4759
4775
  lightApiUrl: `${https}/api/message/tob/sendMessage`,
4760
4776
  notificationIntelligenceLightRulesPluginUrl: lightRulesUrl,
4777
+ notificationIntelligenceLightEffectsSendUrl: lightEffectsSendUrl,
4761
4778
  relayTunnelUrl: `${wss}/message/messages/ws/plugin`,
4762
4779
  appNameMapUrl: `${https}/api/application-config/app-package/config-all`,
4763
4780
  modelProxyLongRecordingSubmitTaskUrl: `${https}/api/model-proxy/long-recording/submit-task`,
@@ -4784,9 +4801,6 @@ function readPersistedEnvName() {
4784
4801
  return;
4785
4802
  }
4786
4803
  function loadEnvName() {
4787
- const fromEnvVar = process.env.PHONE_NOTIFICATIONS_ENV?.trim();
4788
- if (fromEnvVar && VALID_ENVS.has(fromEnvVar))
4789
- return fromEnvVar;
4790
4804
  const fromDotEnv = readPersistedEnvName();
4791
4805
  if (fromDotEnv)
4792
4806
  return fromDotEnv;
@@ -4809,11 +4823,129 @@ function getAvailableEnvs() {
4809
4823
  }
4810
4824
 
4811
4825
  // src/light/sender.ts
4812
- async function sendLightEffect(apiKey, segments, logger, repeatInput, reason, title) {
4826
+ async function sendLightEffect(apiKey, segments, logger, repeatInput, reason, title, options) {
4827
+ const resolvedTitle = resolveLightTitle(title, reason, segments);
4828
+ let repeatTimes;
4829
+ try {
4830
+ repeatTimes = normalizeRepeatTimes(repeatInput);
4831
+ } catch (error) {
4832
+ return { ok: false, error: error?.message ?? String(error) };
4833
+ }
4834
+ const intelligence = await sendViaNotificationIntelligence(apiKey, segments, repeatTimes, resolvedTitle, reason, options?.bizUniqueId, logger);
4835
+ if (!intelligence.fallback) {
4836
+ return intelligence.result;
4837
+ }
4838
+ logger?.warn(`Light sender: notification-intelligence entry unavailable (${intelligence.fallbackReason}), falling back to legacy message-service`);
4839
+ return sendViaLegacyMessageService(apiKey, segments, logger, repeatInput, reason, resolvedTitle);
4840
+ }
4841
+ async function sendViaNotificationIntelligence(apiKey, segments, repeatTimes, title, reason, bizUniqueId, logger) {
4842
+ const url = getEnvUrls().notificationIntelligenceLightEffectsSendUrl;
4843
+ const requestBody = {
4844
+ title,
4845
+ repeat_times: repeatTimes,
4846
+ segments
4847
+ };
4848
+ if (reason?.trim())
4849
+ requestBody.reason = reason;
4850
+ const trimmedBizUniqueId = bizUniqueId?.trim();
4851
+ if (trimmedBizUniqueId)
4852
+ requestBody.bizUniqueId = trimmedBizUniqueId;
4853
+ logger?.info(`Light sender: POST ${url}, bizUniqueId=${trimmedBizUniqueId ?? "(server-generated)"}, ` + `title=${title}, repeat_times=${repeatTimes}, segments_count=${segments.length}`);
4854
+ let res;
4855
+ try {
4856
+ res = await fetch(url, {
4857
+ method: "POST",
4858
+ headers: {
4859
+ "Content-Type": "application/json",
4860
+ "X-Api-Key-Id": stripBearer(apiKey)
4861
+ },
4862
+ body: JSON.stringify(requestBody)
4863
+ });
4864
+ } catch (error) {
4865
+ return {
4866
+ fallback: true,
4867
+ fallbackReason: `network error: ${error?.message ?? String(error)}`,
4868
+ result: { ok: false, error: error?.message ?? String(error) }
4869
+ };
4870
+ }
4871
+ const resBody = await res.text();
4872
+ if (!res.ok) {
4873
+ if (res.status === 404 || res.status === 405 || res.status === 401 && /jwt is missing/i.test(resBody)) {
4874
+ return {
4875
+ fallback: true,
4876
+ fallbackReason: `HTTP ${res.status}`,
4877
+ result: { ok: false, status: res.status, error: resBody }
4878
+ };
4879
+ }
4880
+ logger?.warn(`Light sender: FAILED ${res.status}, url=${url}, resBody=${resBody.substring(0, 500)}`);
4881
+ return {
4882
+ fallback: false,
4883
+ result: {
4884
+ ok: false,
4885
+ status: res.status,
4886
+ error: resBody,
4887
+ via: "notification-intelligence"
4888
+ }
4889
+ };
4890
+ }
4891
+ let parsed = null;
4892
+ try {
4893
+ parsed = resBody.trim() ? JSON.parse(resBody) : null;
4894
+ } catch {}
4895
+ if (!parsed || typeof parsed !== "object") {
4896
+ logger?.warn(`Light sender: invalid JSON from ${url}, resBody=${resBody.substring(0, 200)}`);
4897
+ return {
4898
+ fallback: false,
4899
+ result: {
4900
+ ok: false,
4901
+ error: "invalid JSON response",
4902
+ via: "notification-intelligence"
4903
+ }
4904
+ };
4905
+ }
4906
+ const data = parsed.data ?? null;
4907
+ if (parsed.code && parsed.code !== "000000") {
4908
+ const message = parsed.msg ?? parsed.message ?? `remote code ${parsed.code}`;
4909
+ logger?.warn(`Light sender: FAILED code=${parsed.code}, msg=${message}`);
4910
+ return {
4911
+ fallback: false,
4912
+ result: {
4913
+ ok: false,
4914
+ error: String(message),
4915
+ response: parsed,
4916
+ via: "notification-intelligence"
4917
+ }
4918
+ };
4919
+ }
4920
+ if (data?.success === false) {
4921
+ const message = data.message ?? "remote business request failed";
4922
+ logger?.warn(`Light sender: FAILED business, message=${message}`);
4923
+ return {
4924
+ fallback: false,
4925
+ result: {
4926
+ ok: false,
4927
+ error: String(message),
4928
+ response: parsed,
4929
+ via: "notification-intelligence"
4930
+ }
4931
+ };
4932
+ }
4933
+ const effectiveBizUniqueId = typeof data?.bizUniqueId === "string" && data.bizUniqueId.trim() || trimmedBizUniqueId;
4934
+ logger?.info(`Light sender: OK bizUniqueId=${effectiveBizUniqueId ?? "-"}, resBody=${resBody.substring(0, 200)}`);
4935
+ return {
4936
+ fallback: false,
4937
+ result: {
4938
+ ok: true,
4939
+ bizUniqueId: effectiveBizUniqueId || undefined,
4940
+ response: parsed,
4941
+ via: "notification-intelligence"
4942
+ }
4943
+ };
4944
+ }
4945
+ async function sendViaLegacyMessageService(apiKey, segments, logger, repeatInput, reason, resolvedTitle) {
4813
4946
  const apiUrl = getEnvUrls().lightApiUrl;
4814
4947
  const appKey = "phone-notifications";
4815
4948
  const templateId = "1990771146010017800";
4816
- const resolvedTitle = resolveLightTitle(title, reason, segments);
4817
4949
  logger?.info(`Light sender: apiUrl=${apiUrl ?? "UNSET"}, appKey=${appKey ? appKey.substring(0, 8) + "…" : "UNSET"}, templateId=${templateId ?? "UNSET"}, apiKey=${apiKey ? apiKey.substring(0, 20) + "…" : "EMPTY"}, title=${resolvedTitle}, reason=${reason ?? ""}, segments=${JSON.stringify(segments)}`);
4818
4950
  if (!apiUrl || !appKey || !templateId) {
4819
4951
  return {
@@ -4841,17 +4973,25 @@ async function sendLightEffect(apiKey, segments, logger, repeatInput, reason, ti
4841
4973
  method: "POST",
4842
4974
  headers: {
4843
4975
  "Content-Type": "application/json",
4844
- "X-Api-Key-Id": apiKey.startsWith("Bearer ") ? apiKey.slice("Bearer ".length) : apiKey
4976
+ "X-Api-Key-Id": stripBearer(apiKey)
4845
4977
  },
4846
4978
  body: JSON.stringify(requestBody)
4847
4979
  });
4848
4980
  const resBody = await res.text();
4849
4981
  if (!res.ok) {
4850
4982
  logger?.warn(`Light sender: FAILED ${res.status}, url=${apiUrl}, resBody=${resBody.substring(0, 500)}`);
4851
- return { ok: false, status: res.status, error: resBody };
4983
+ return { ok: false, status: res.status, error: resBody, via: "message-service" };
4852
4984
  }
4853
4985
  logger?.info(`Light sender: OK bizUniqueId=${bizUniqueId}, resBody=${resBody.substring(0, 200)}`);
4854
- return { ok: true, bizUniqueId, response: JSON.parse(resBody) };
4986
+ return {
4987
+ ok: true,
4988
+ bizUniqueId,
4989
+ response: JSON.parse(resBody),
4990
+ via: "message-service"
4991
+ };
4992
+ }
4993
+ function stripBearer(apiKey) {
4994
+ return apiKey.startsWith("Bearer ") ? apiKey.slice("Bearer ".length) : apiKey;
4855
4995
  }
4856
4996
  function resolveLightTitle(title, reason, segments) {
4857
4997
  const trimmedTitle = title?.trim();
@@ -4968,7 +5108,7 @@ function upsertToolsForPolicy(policy, tools) {
4968
5108
  function resolveConfigPath2() {
4969
5109
  return resolveConfigPath();
4970
5110
  }
4971
- var LIGHT_TOOLS = [...LIGHT_RULE_TOOL_NAME_LIST];
5111
+ var LIGHT_TOOLS = ["light_control", ...LIGHT_RULE_TOOL_NAME_LIST];
4972
5112
  function upsertLightRuleToolsAlsoAllow(cfg) {
4973
5113
  if (!isObject(cfg.tools))
4974
5114
  cfg.tools = {};
@@ -4988,7 +5128,7 @@ function upsertLightRuleToolsAlsoAllow(cfg) {
4988
5128
  return { globalChanged, mainAgentChanged };
4989
5129
  }
4990
5130
  function registerLightSetupTools(light) {
4991
- light.command("setup").description("自动放行云端灯效规则工具(兼容 tools.allow / tools.alsoAllow)").action(() => {
5131
+ light.command("setup").description("自动放行一次性灯效与云端灯效规则工具(兼容 tools.allow / tools.alsoAllow)").action(() => {
4992
5132
  const configPath = resolveConfigPath2();
4993
5133
  if (!import_node_fs10.existsSync(configPath)) {
4994
5134
  exitError("CONFIG_NOT_FOUND", `未找到配置文件: ${configPath}`);
@@ -5685,6 +5825,180 @@ var checkTunnel = ({ stateDir }) => {
5685
5825
  };
5686
5826
  };
5687
5827
 
5828
+ // src/cli/doctor/check-local-gateway.ts
5829
+ var import_node_net = __toESM(require("node:net"));
5830
+
5831
+ // src/tunnel/gateway-target.ts
5832
+ var DEFAULT_GATEWAY_PORT = "18789";
5833
+ var DEFAULT_GATEWAY_HOST = "127.0.0.1";
5834
+ var BASE_URL_ENV_KEYS = [
5835
+ "PHONE_NOTIFICATIONS_GATEWAY_BASE_URL",
5836
+ "OPENCLAW_GATEWAY_BASE_URL",
5837
+ "QCLAW_GATEWAY_BASE_URL"
5838
+ ];
5839
+ var PORT_ENV_KEYS = [
5840
+ "OPENCLAW_GATEWAY_PORT",
5841
+ "QCLAW_GATEWAY_PORT"
5842
+ ];
5843
+ function trimToUndefined2(value) {
5844
+ if (typeof value !== "string")
5845
+ return;
5846
+ const trimmed = value.trim();
5847
+ return trimmed || undefined;
5848
+ }
5849
+ function firstEnv(env, keys) {
5850
+ for (const key of keys) {
5851
+ const value = trimToUndefined2(env[key]);
5852
+ if (value)
5853
+ return { key, value };
5854
+ }
5855
+ return;
5856
+ }
5857
+ function normalizePort(value) {
5858
+ const raw = typeof value === "number" && Number.isInteger(value) ? String(value) : trimToUndefined2(value);
5859
+ if (!raw || !/^\d+$/.test(raw))
5860
+ return;
5861
+ const port = Number.parseInt(raw, 10);
5862
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
5863
+ return;
5864
+ }
5865
+ return String(port);
5866
+ }
5867
+ function normalizeBaseUrl(raw) {
5868
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `http://${raw}`;
5869
+ let parsed;
5870
+ try {
5871
+ parsed = new URL(withScheme);
5872
+ } catch {
5873
+ return;
5874
+ }
5875
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
5876
+ return;
5877
+ }
5878
+ if (!parsed.hostname)
5879
+ return;
5880
+ return `${parsed.protocol}//${parsed.host}`;
5881
+ }
5882
+ function isGatewayTlsEnabled(config) {
5883
+ return config.gateway?.tls?.enabled === true;
5884
+ }
5885
+ function resolveGatewayPort(params) {
5886
+ const envPort = firstEnv(params.env, PORT_ENV_KEYS);
5887
+ const normalizedEnvPort = envPort ? normalizePort(envPort.value) : undefined;
5888
+ if (envPort && normalizedEnvPort) {
5889
+ return { port: normalizedEnvPort, source: envPort.key };
5890
+ }
5891
+ const configPort = normalizePort(params.config.gateway?.port);
5892
+ if (configPort) {
5893
+ return { port: configPort, source: "openclaw.json gateway.port" };
5894
+ }
5895
+ return { port: DEFAULT_GATEWAY_PORT, source: "default" };
5896
+ }
5897
+ function resolveLocalGatewayBaseUrl(params = {}) {
5898
+ const config = params.config ?? {};
5899
+ const env = params.env ?? process.env;
5900
+ const tlsEnabled = isGatewayTlsEnabled(config);
5901
+ const envBaseUrl = firstEnv(env, BASE_URL_ENV_KEYS);
5902
+ if (envBaseUrl) {
5903
+ const normalized = normalizeBaseUrl(envBaseUrl.value);
5904
+ if (normalized) {
5905
+ return {
5906
+ baseUrl: normalized,
5907
+ source: envBaseUrl.key,
5908
+ explicit: true,
5909
+ tlsEnabled
5910
+ };
5911
+ }
5912
+ }
5913
+ const { port, source } = resolveGatewayPort({ config, env });
5914
+ const baseUrl = `http://${DEFAULT_GATEWAY_HOST}:${port}`;
5915
+ const warning = envBaseUrl ? `忽略无效的 ${envBaseUrl.key}=${envBaseUrl.value}` : undefined;
5916
+ return {
5917
+ baseUrl,
5918
+ source,
5919
+ explicit: false,
5920
+ tlsEnabled,
5921
+ warning
5922
+ };
5923
+ }
5924
+ function resolveLocalGatewayTcpTarget(baseUrl) {
5925
+ let parsed;
5926
+ try {
5927
+ parsed = new URL(baseUrl);
5928
+ } catch {
5929
+ return null;
5930
+ }
5931
+ const port = parsed.port ? Number.parseInt(parsed.port, 10) : parsed.protocol === "https:" ? 443 : parsed.protocol === "http:" ? 80 : Number.NaN;
5932
+ if (!parsed.hostname || !Number.isInteger(port))
5933
+ return null;
5934
+ return { host: parsed.hostname, port };
5935
+ }
5936
+
5937
+ // src/cli/doctor/check-local-gateway.ts
5938
+ var PROBE_TIMEOUT_MS = 1200;
5939
+ function probeTcp(host, port) {
5940
+ return new Promise((resolve2) => {
5941
+ const socket = import_node_net.default.connect({ host, port });
5942
+ let settled = false;
5943
+ const finish = (result) => {
5944
+ if (settled)
5945
+ return;
5946
+ settled = true;
5947
+ socket.destroy();
5948
+ resolve2(result);
5949
+ };
5950
+ socket.setTimeout(PROBE_TIMEOUT_MS);
5951
+ socket.once("connect", () => finish({ ok: true }));
5952
+ socket.once("timeout", () => finish({ ok: false, error: "timeout" }));
5953
+ socket.once("error", (err) => finish({ ok: false, error: err.message }));
5954
+ });
5955
+ }
5956
+ var checkLocalGateway = async ({ cfg }) => {
5957
+ const resolution = resolveLocalGatewayBaseUrl({ config: cfg });
5958
+ if (resolution.warning) {
5959
+ return {
5960
+ id: "local-gateway",
5961
+ severity: "warn",
5962
+ title: "本地 Gateway 地址配置无效",
5963
+ detail: `${resolution.warning},当前回退到 ${resolution.baseUrl}`,
5964
+ fixDescription: "请修正 PHONE_NOTIFICATIONS_GATEWAY_BASE_URL,或清理该环境变量后重启 openclaw/gateway",
5965
+ fix: null
5966
+ };
5967
+ }
5968
+ if (resolution.tlsEnabled && !resolution.explicit) {
5969
+ return {
5970
+ id: "local-gateway",
5971
+ severity: "warn",
5972
+ title: "Gateway TLS 可能阻断 Relay 本地代理",
5973
+ detail: `检测到 gateway.tls.enabled=true,但插件本地代理默认连接 ${resolution.baseUrl}。` + "如果 Gateway 只接受 HTTPS/WSS,本地 RPC 握手会失败。",
5974
+ fixDescription: "设置 PHONE_NOTIFICATIONS_GATEWAY_BASE_URL=https://127.0.0.1:<port>,或关闭 gateway.tls.enabled 后重启 gateway",
5975
+ fix: null
5976
+ };
5977
+ }
5978
+ const target = resolveLocalGatewayTcpTarget(resolution.baseUrl);
5979
+ if (!target) {
5980
+ return {
5981
+ id: "local-gateway",
5982
+ severity: "warn",
5983
+ title: "本地 Gateway 地址无法解析",
5984
+ detail: `无法解析插件将使用的本地 Gateway 地址: ${resolution.baseUrl}`,
5985
+ fixDescription: "请设置 PHONE_NOTIFICATIONS_GATEWAY_BASE_URL=http://127.0.0.1:<port>",
5986
+ fix: null
5987
+ };
5988
+ }
5989
+ const probe = await probeTcp(target.host, target.port);
5990
+ if (probe.ok)
5991
+ return null;
5992
+ return {
5993
+ id: "local-gateway",
5994
+ severity: "warn",
5995
+ title: "本地 Gateway 不可达",
5996
+ detail: `插件将连接 ${resolution.baseUrl},但 TCP 探测失败` + (probe.error ? `: ${probe.error}` : ""),
5997
+ fixDescription: "请执行 openclaw gateway status,确认 gateway.port 与 PHONE_NOTIFICATIONS_GATEWAY_BASE_URL 是否一致",
5998
+ fix: null
5999
+ };
6000
+ };
6001
+
5688
6002
  // src/cli/doctor/check-node-version.ts
5689
6003
  var MIN_MAJOR = 22;
5690
6004
  var MIN_MINOR = 12;
@@ -5792,7 +6106,7 @@ function pickNewestVersion(versions) {
5792
6106
  var import_node_fs15 = require("node:fs");
5793
6107
  function readBuildInjectedVersion() {
5794
6108
  if (false) {}
5795
- const version = "1.12.2".trim();
6109
+ const version = "1.12.4".trim();
5796
6110
  return version || undefined;
5797
6111
  }
5798
6112
  function readPluginVersionFromPackageJson() {
@@ -5847,6 +6161,7 @@ var allCheckers = [
5847
6161
  checkStateDirPerms,
5848
6162
  checkToolPolicy,
5849
6163
  checkTunnel,
6164
+ checkLocalGateway,
5850
6165
  checkNodeVersion,
5851
6166
  checkPluginVersion
5852
6167
  ];
@@ -6456,5 +6771,5 @@ program2.parseAsync(process.argv).catch((err) => {
6456
6771
  process.exit(1);
6457
6772
  });
6458
6773
 
6459
- //# debugId=5548672AE9D3231064756E2164756E21
6774
+ //# debugId=BC4E61ACFD730DE964756E2164756E21
6460
6775
  //# sourceMappingURL=ntf.cjs.map