@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.
- package/README.md +3 -4
- package/dist/bin/ntf.cjs +327 -12
- 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 +16 -1
- 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 +2444 -1911
- package/dist/index.cjs.map +38 -31
- 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/recordings.d.ts +0 -2
- package/dist/plugin/recordings.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/downloader.d.ts +0 -7
- package/dist/recording/downloader.d.ts.map +1 -1
- package/dist/recording/index.d.ts +2 -4
- package/dist/recording/index.d.ts.map +1 -1
- package/dist/recording/result-writer.d.ts +1 -2
- package/dist/recording/result-writer.d.ts.map +1 -1
- package/dist/recording/storage.d.ts +3 -2
- 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 +97 -2
- 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/relay-client.d.ts.map +1 -1
- package/dist/tunnel/service.d.ts.map +1 -1
- package/dist/types.d.ts +12 -67
- package/dist/types.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 +11 -0
- 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/dist/recording/asr.d.ts +0 -119
- package/dist/recording/asr.d.ts.map +0 -1
- package/dist/recording/handler.d.ts +0 -38
- package/dist/recording/handler.d.ts.map +0 -1
- package/dist/recording/whisper-local.d.ts +0 -81
- 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` /
|
|
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`,
|
|
@@ -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":
|
|
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 {
|
|
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("
|
|
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.
|
|
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=
|
|
6774
|
+
//# debugId=BC4E61ACFD730DE964756E2164756E21
|
|
6460
6775
|
//# sourceMappingURL=ntf.cjs.map
|