@yoooclaw/phone-notifications 1.12.3 → 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.
- package/README.md +3 -4
- package/dist/bin/ntf.cjs +327 -9
- package/dist/bin/ntf.cjs.map +9 -7
- package/dist/cli/doctor/check-local-gateway.d.ts +3 -0
- package/dist/cli/doctor/check-local-gateway.d.ts.map +1 -0
- package/dist/cli/doctor/checkers.d.ts.map +1 -1
- package/dist/env.d.ts +8 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/fs-atomic.d.ts +11 -0
- package/dist/fs-atomic.d.ts.map +1 -0
- package/dist/image/store.d.ts.map +1 -1
- package/dist/index.cjs +2084 -740
- package/dist/index.cjs.map +34 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/light/sender.d.ts +14 -3
- package/dist/light/sender.d.ts.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/notification/storage.d.ts +4 -3
- package/dist/notification/storage.d.ts.map +1 -1
- package/dist/plugin/auto-update.d.ts +2 -0
- package/dist/plugin/auto-update.d.ts.map +1 -1
- package/dist/plugin/lifecycle.d.ts +7 -14
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/light-control.d.ts.map +1 -1
- package/dist/plugin/light-rules-tools.d.ts.map +1 -1
- package/dist/plugin/notifications.d.ts +1 -0
- package/dist/plugin/notifications.d.ts.map +1 -1
- package/dist/plugin/register-guard.d.ts +16 -0
- package/dist/plugin/register-guard.d.ts.map +1 -0
- package/dist/plugin/runtime.d.ts +64 -0
- package/dist/plugin/runtime.d.ts.map +1 -0
- package/dist/plugin/shared-storage.d.ts +31 -0
- package/dist/plugin/shared-storage.d.ts.map +1 -0
- package/dist/plugin/system.d.ts +17 -0
- package/dist/plugin/system.d.ts.map +1 -0
- package/dist/recording/storage.d.ts +2 -1
- package/dist/recording/storage.d.ts.map +1 -1
- package/dist/system/os-info.d.ts +34 -0
- package/dist/system/os-info.d.ts.map +1 -0
- package/dist/tunnel/connect-status-reporter.d.ts.map +1 -1
- package/dist/tunnel/frame-slimmer.d.ts +82 -1
- package/dist/tunnel/frame-slimmer.d.ts.map +1 -1
- package/dist/tunnel/gateway-target.d.ts +17 -0
- package/dist/tunnel/gateway-target.d.ts.map +1 -0
- package/dist/tunnel/http-proxy.d.ts.map +1 -1
- package/dist/tunnel/proxy.d.ts +20 -1
- package/dist/tunnel/proxy.d.ts.map +1 -1
- package/dist/tunnel/service.d.ts.map +1 -1
- package/dist/update/executor.d.ts +3 -3
- package/dist/update/executor.d.ts.map +1 -1
- package/dist/update/index.d.ts +3 -2
- package/dist/update/index.d.ts.map +1 -1
- package/dist/update/stale-runtime.d.ts +43 -0
- package/dist/update/stale-runtime.d.ts.map +1 -0
- package/openclaw.plugin.json +1 -0
- package/package.json +1 -1
- package/skills/notification-monitor/SKILL.md +2 -2
- package/skills/notification-to-memory/SKILL.md +2 -2
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` /
|
|
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
|
-
#
|
|
665
|
+
# 启用一次性灯效及云端灯效规则工具
|
|
667
666
|
openclaw ntf light setup
|
|
668
667
|
|
|
669
|
-
#
|
|
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`,
|
|
@@ -4806,11 +4823,129 @@ function getAvailableEnvs() {
|
|
|
4806
4823
|
}
|
|
4807
4824
|
|
|
4808
4825
|
// src/light/sender.ts
|
|
4809
|
-
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) {
|
|
4810
4946
|
const apiUrl = getEnvUrls().lightApiUrl;
|
|
4811
4947
|
const appKey = "phone-notifications";
|
|
4812
4948
|
const templateId = "1990771146010017800";
|
|
4813
|
-
const resolvedTitle = resolveLightTitle(title, reason, segments);
|
|
4814
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)}`);
|
|
4815
4950
|
if (!apiUrl || !appKey || !templateId) {
|
|
4816
4951
|
return {
|
|
@@ -4838,17 +4973,25 @@ async function sendLightEffect(apiKey, segments, logger, repeatInput, reason, ti
|
|
|
4838
4973
|
method: "POST",
|
|
4839
4974
|
headers: {
|
|
4840
4975
|
"Content-Type": "application/json",
|
|
4841
|
-
"X-Api-Key-Id":
|
|
4976
|
+
"X-Api-Key-Id": stripBearer(apiKey)
|
|
4842
4977
|
},
|
|
4843
4978
|
body: JSON.stringify(requestBody)
|
|
4844
4979
|
});
|
|
4845
4980
|
const resBody = await res.text();
|
|
4846
4981
|
if (!res.ok) {
|
|
4847
4982
|
logger?.warn(`Light sender: FAILED ${res.status}, url=${apiUrl}, resBody=${resBody.substring(0, 500)}`);
|
|
4848
|
-
return { ok: false, status: res.status, error: resBody };
|
|
4983
|
+
return { ok: false, status: res.status, error: resBody, via: "message-service" };
|
|
4849
4984
|
}
|
|
4850
4985
|
logger?.info(`Light sender: OK bizUniqueId=${bizUniqueId}, resBody=${resBody.substring(0, 200)}`);
|
|
4851
|
-
return {
|
|
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;
|
|
4852
4995
|
}
|
|
4853
4996
|
function resolveLightTitle(title, reason, segments) {
|
|
4854
4997
|
const trimmedTitle = title?.trim();
|
|
@@ -4965,7 +5108,7 @@ function upsertToolsForPolicy(policy, tools) {
|
|
|
4965
5108
|
function resolveConfigPath2() {
|
|
4966
5109
|
return resolveConfigPath();
|
|
4967
5110
|
}
|
|
4968
|
-
var LIGHT_TOOLS = [...LIGHT_RULE_TOOL_NAME_LIST];
|
|
5111
|
+
var LIGHT_TOOLS = ["light_control", ...LIGHT_RULE_TOOL_NAME_LIST];
|
|
4969
5112
|
function upsertLightRuleToolsAlsoAllow(cfg) {
|
|
4970
5113
|
if (!isObject(cfg.tools))
|
|
4971
5114
|
cfg.tools = {};
|
|
@@ -4985,7 +5128,7 @@ function upsertLightRuleToolsAlsoAllow(cfg) {
|
|
|
4985
5128
|
return { globalChanged, mainAgentChanged };
|
|
4986
5129
|
}
|
|
4987
5130
|
function registerLightSetupTools(light) {
|
|
4988
|
-
light.command("setup").description("
|
|
5131
|
+
light.command("setup").description("自动放行一次性灯效与云端灯效规则工具(兼容 tools.allow / tools.alsoAllow)").action(() => {
|
|
4989
5132
|
const configPath = resolveConfigPath2();
|
|
4990
5133
|
if (!import_node_fs10.existsSync(configPath)) {
|
|
4991
5134
|
exitError("CONFIG_NOT_FOUND", `未找到配置文件: ${configPath}`);
|
|
@@ -5682,6 +5825,180 @@ var checkTunnel = ({ stateDir }) => {
|
|
|
5682
5825
|
};
|
|
5683
5826
|
};
|
|
5684
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
|
+
|
|
5685
6002
|
// src/cli/doctor/check-node-version.ts
|
|
5686
6003
|
var MIN_MAJOR = 22;
|
|
5687
6004
|
var MIN_MINOR = 12;
|
|
@@ -5789,7 +6106,7 @@ function pickNewestVersion(versions) {
|
|
|
5789
6106
|
var import_node_fs15 = require("node:fs");
|
|
5790
6107
|
function readBuildInjectedVersion() {
|
|
5791
6108
|
if (false) {}
|
|
5792
|
-
const version = "1.12.
|
|
6109
|
+
const version = "1.12.4".trim();
|
|
5793
6110
|
return version || undefined;
|
|
5794
6111
|
}
|
|
5795
6112
|
function readPluginVersionFromPackageJson() {
|
|
@@ -5844,6 +6161,7 @@ var allCheckers = [
|
|
|
5844
6161
|
checkStateDirPerms,
|
|
5845
6162
|
checkToolPolicy,
|
|
5846
6163
|
checkTunnel,
|
|
6164
|
+
checkLocalGateway,
|
|
5847
6165
|
checkNodeVersion,
|
|
5848
6166
|
checkPluginVersion
|
|
5849
6167
|
];
|
|
@@ -6453,5 +6771,5 @@ program2.parseAsync(process.argv).catch((err) => {
|
|
|
6453
6771
|
process.exit(1);
|
|
6454
6772
|
});
|
|
6455
6773
|
|
|
6456
|
-
//# debugId=
|
|
6774
|
+
//# debugId=BC4E61ACFD730DE964756E2164756E21
|
|
6457
6775
|
//# sourceMappingURL=ntf.cjs.map
|