@yoooclaw/phone-notifications 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +391 -160
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3647,8 +3647,8 @@ var require_websocket_server = __commonJS({
|
|
|
3647
3647
|
});
|
|
3648
3648
|
|
|
3649
3649
|
// src/index.ts
|
|
3650
|
-
import { readFileSync as
|
|
3651
|
-
import { basename, dirname as
|
|
3650
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
3651
|
+
import { basename as basename2, dirname as dirname5 } from "path";
|
|
3652
3652
|
|
|
3653
3653
|
// src/light/protocol.ts
|
|
3654
3654
|
var MAX_LIGHT_SEGMENTS = 12;
|
|
@@ -3791,8 +3791,56 @@ function quantizeWindow(value) {
|
|
|
3791
3791
|
|
|
3792
3792
|
// src/light/sender.ts
|
|
3793
3793
|
import { randomUUID } from "crypto";
|
|
3794
|
+
|
|
3795
|
+
// src/env.ts
|
|
3796
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3797
|
+
import { join, dirname } from "path";
|
|
3798
|
+
var ENV_CONFIG = {
|
|
3799
|
+
development: {
|
|
3800
|
+
lightApiUrl: "https://openclaw-service-dev.yoootek.com/api/message/tob/sendMessage",
|
|
3801
|
+
relayTunnelUrl: "wss://openclaw-service-dev.yoootek.com/message/messages/ws/plugin"
|
|
3802
|
+
},
|
|
3803
|
+
production: {
|
|
3804
|
+
lightApiUrl: "https://openclaw-service.yoootek.com/api/message/tob/sendMessage",
|
|
3805
|
+
relayTunnelUrl: "wss://openclaw-service.yoootek.com/message/messages/ws/plugin"
|
|
3806
|
+
}
|
|
3807
|
+
};
|
|
3808
|
+
var VALID_ENVS = new Set(Object.keys(ENV_CONFIG));
|
|
3809
|
+
function envFilePath() {
|
|
3810
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
3811
|
+
return join(home, ".openclaw", "env.json");
|
|
3812
|
+
}
|
|
3813
|
+
function loadEnvName() {
|
|
3814
|
+
const filePath = envFilePath();
|
|
3815
|
+
if (!existsSync(filePath)) return "development";
|
|
3816
|
+
try {
|
|
3817
|
+
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
3818
|
+
if (data.env && VALID_ENVS.has(data.env)) return data.env;
|
|
3819
|
+
} catch {
|
|
3820
|
+
}
|
|
3821
|
+
return "development";
|
|
3822
|
+
}
|
|
3823
|
+
function saveEnvName(env) {
|
|
3824
|
+
if (!VALID_ENVS.has(env)) {
|
|
3825
|
+
throw new Error(`\u65E0\u6548\u7684\u73AF\u5883\u540D\u79F0: ${env}\uFF0C\u53EF\u9009\u503C: ${[...VALID_ENVS].join(", ")}`);
|
|
3826
|
+
}
|
|
3827
|
+
const filePath = envFilePath();
|
|
3828
|
+
mkdirSync(dirname(filePath), { recursive: true, mode: 448 });
|
|
3829
|
+
writeFileSync(filePath, JSON.stringify({ env }, null, 2), {
|
|
3830
|
+
encoding: "utf-8",
|
|
3831
|
+
mode: 384
|
|
3832
|
+
});
|
|
3833
|
+
}
|
|
3834
|
+
function getEnvUrls(env) {
|
|
3835
|
+
return ENV_CONFIG[env ?? loadEnvName()];
|
|
3836
|
+
}
|
|
3837
|
+
function getAvailableEnvs() {
|
|
3838
|
+
return Object.keys(ENV_CONFIG);
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
// src/light/sender.ts
|
|
3794
3842
|
async function sendLightEffect(token, segments, logger, repeat) {
|
|
3795
|
-
const apiUrl =
|
|
3843
|
+
const apiUrl = getEnvUrls().lightApiUrl;
|
|
3796
3844
|
const appKey = "7Q617S1G5WD274JI";
|
|
3797
3845
|
const templateId = "1990771146010017788";
|
|
3798
3846
|
logger?.info(
|
|
@@ -3836,27 +3884,203 @@ async function sendLightEffect(token, segments, logger, repeat) {
|
|
|
3836
3884
|
return { ok: true, bizUniqueId, response: JSON.parse(resBody) };
|
|
3837
3885
|
}
|
|
3838
3886
|
|
|
3887
|
+
// src/notification/app-name-map.ts
|
|
3888
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
3889
|
+
import { join as join3 } from "path";
|
|
3890
|
+
|
|
3891
|
+
// src/auth/credentials.ts
|
|
3892
|
+
import {
|
|
3893
|
+
existsSync as existsSync2,
|
|
3894
|
+
mkdirSync as mkdirSync2,
|
|
3895
|
+
readFileSync as readFileSync2,
|
|
3896
|
+
writeFileSync as writeFileSync2,
|
|
3897
|
+
watch
|
|
3898
|
+
} from "fs";
|
|
3899
|
+
import { join as join2, dirname as dirname2, basename } from "path";
|
|
3900
|
+
function credentialsPath() {
|
|
3901
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
3902
|
+
return join2(home, ".openclaw", "credentials.json");
|
|
3903
|
+
}
|
|
3904
|
+
function readCredentials() {
|
|
3905
|
+
const path2 = credentialsPath();
|
|
3906
|
+
if (!existsSync2(path2)) return {};
|
|
3907
|
+
try {
|
|
3908
|
+
return JSON.parse(readFileSync2(path2, "utf-8"));
|
|
3909
|
+
} catch {
|
|
3910
|
+
return {};
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
function writeCredentials(creds) {
|
|
3914
|
+
const path2 = credentialsPath();
|
|
3915
|
+
mkdirSync2(dirname2(path2), { recursive: true, mode: 448 });
|
|
3916
|
+
writeFileSync2(path2, JSON.stringify(creds, null, 2), {
|
|
3917
|
+
encoding: "utf-8",
|
|
3918
|
+
mode: 384
|
|
3919
|
+
});
|
|
3920
|
+
}
|
|
3921
|
+
function loadToken() {
|
|
3922
|
+
return readCredentials().token;
|
|
3923
|
+
}
|
|
3924
|
+
function requireToken() {
|
|
3925
|
+
const token = loadToken();
|
|
3926
|
+
if (!token) {
|
|
3927
|
+
throw new Error(
|
|
3928
|
+
"Token \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u5148\u6267\u884C openclaw ntf auth set-token <token>\uFF08\u82E5 ntf \u547D\u4EE4\u51B2\u7A81\uFF0C\u53EF\u4F7F\u7528 openclaw phone-notifications auth set-token <token>\uFF09"
|
|
3929
|
+
);
|
|
3930
|
+
}
|
|
3931
|
+
return token;
|
|
3932
|
+
}
|
|
3933
|
+
function watchCredentials(onChange) {
|
|
3934
|
+
const path2 = credentialsPath();
|
|
3935
|
+
const dir = dirname2(path2);
|
|
3936
|
+
const filename = basename(path2);
|
|
3937
|
+
let debounceTimer = null;
|
|
3938
|
+
const delayMs = 200;
|
|
3939
|
+
const listener = (_event, changedName) => {
|
|
3940
|
+
if (!changedName || changedName !== filename && !changedName.endsWith(filename)) return;
|
|
3941
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
3942
|
+
debounceTimer = setTimeout(() => {
|
|
3943
|
+
debounceTimer = null;
|
|
3944
|
+
onChange();
|
|
3945
|
+
}, delayMs);
|
|
3946
|
+
};
|
|
3947
|
+
let watcher = null;
|
|
3948
|
+
try {
|
|
3949
|
+
watcher = watch(dir, { persistent: false }, listener);
|
|
3950
|
+
} catch {
|
|
3951
|
+
}
|
|
3952
|
+
return () => {
|
|
3953
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
3954
|
+
watcher?.close();
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
// src/notification/app-name-map.ts
|
|
3959
|
+
var PLUGIN_STATE_DIR = "phone-notifications";
|
|
3960
|
+
var CACHE_FILE = "app-name-map.json";
|
|
3961
|
+
var BUILTIN_APP_NAME_MAP_URL = "https://openclaw-service-dev.yoootek.com/api/application-config/app-package/config-all";
|
|
3962
|
+
var APP_NAME_MAP_URL = BUILTIN_APP_NAME_MAP_URL;
|
|
3963
|
+
var APP_NAME_MAP_REFRESH_HOURS = 12;
|
|
3964
|
+
function isRecordOfStrings(v) {
|
|
3965
|
+
if (v === null || typeof v !== "object") return false;
|
|
3966
|
+
for (const val of Object.values(v)) if (typeof val !== "string") return false;
|
|
3967
|
+
return true;
|
|
3968
|
+
}
|
|
3969
|
+
function isAppNameMapApiResponse(v) {
|
|
3970
|
+
if (v === null || typeof v !== "object") return false;
|
|
3971
|
+
const o = v;
|
|
3972
|
+
return Array.isArray(o.data) && o.data.every(
|
|
3973
|
+
(item) => item !== null && typeof item === "object" && typeof item.packageName === "string" && typeof item.appName === "string"
|
|
3974
|
+
);
|
|
3975
|
+
}
|
|
3976
|
+
function getCachePath(stateDir) {
|
|
3977
|
+
return join3(stateDir, "plugins", PLUGIN_STATE_DIR, CACHE_FILE);
|
|
3978
|
+
}
|
|
3979
|
+
function createAppNameMapProvider(opts) {
|
|
3980
|
+
const { stateDir, logger } = opts;
|
|
3981
|
+
const url = APP_NAME_MAP_URL;
|
|
3982
|
+
const refreshHours = APP_NAME_MAP_REFRESH_HOURS;
|
|
3983
|
+
const map = /* @__PURE__ */ new Map();
|
|
3984
|
+
let refreshTimer = null;
|
|
3985
|
+
let stopWatching = null;
|
|
3986
|
+
let inFlightFetch = null;
|
|
3987
|
+
function loadFromDisk() {
|
|
3988
|
+
const path2 = getCachePath(stateDir);
|
|
3989
|
+
if (!existsSync3(path2)) return;
|
|
3990
|
+
try {
|
|
3991
|
+
const raw = JSON.parse(readFileSync3(path2, "utf-8"));
|
|
3992
|
+
if (!isRecordOfStrings(raw)) return;
|
|
3993
|
+
map.clear();
|
|
3994
|
+
for (const [k, v] of Object.entries(raw)) map.set(k, v);
|
|
3995
|
+
} catch {
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
async function fetchFromServer() {
|
|
3999
|
+
const token = loadToken();
|
|
4000
|
+
if (!token || !url) return;
|
|
4001
|
+
const auth = token.startsWith("Bearer ") ? token : `Bearer ${token}`;
|
|
4002
|
+
try {
|
|
4003
|
+
const res = await fetch(url, {
|
|
4004
|
+
method: "POST",
|
|
4005
|
+
headers: { "Content-Type": "application/json", Authorization: auth },
|
|
4006
|
+
body: JSON.stringify({
|
|
4007
|
+
platform: ""
|
|
4008
|
+
})
|
|
4009
|
+
});
|
|
4010
|
+
if (!res.ok) {
|
|
4011
|
+
return;
|
|
4012
|
+
}
|
|
4013
|
+
const body = await res.json();
|
|
4014
|
+
if (!isAppNameMapApiResponse(body) || !body.success || !body.data?.length) {
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
4017
|
+
map.clear();
|
|
4018
|
+
for (const item of body.data) {
|
|
4019
|
+
if (item.packageName && item.appName) map.set(item.packageName, item.appName);
|
|
4020
|
+
}
|
|
4021
|
+
const dir = join3(stateDir, "plugins", PLUGIN_STATE_DIR);
|
|
4022
|
+
mkdirSync3(dir, { recursive: true });
|
|
4023
|
+
writeFileSync3(getCachePath(stateDir), JSON.stringify(Object.fromEntries(map), null, 2), "utf-8");
|
|
4024
|
+
} catch (e) {
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
async function ensureOneFetch() {
|
|
4028
|
+
if (inFlightFetch) return inFlightFetch;
|
|
4029
|
+
inFlightFetch = fetchFromServer().finally(() => {
|
|
4030
|
+
inFlightFetch = null;
|
|
4031
|
+
});
|
|
4032
|
+
return inFlightFetch;
|
|
4033
|
+
}
|
|
4034
|
+
return {
|
|
4035
|
+
async resolveDisplayName(packageName) {
|
|
4036
|
+
if (map.has(packageName)) return map.get(packageName);
|
|
4037
|
+
return packageName;
|
|
4038
|
+
},
|
|
4039
|
+
async start() {
|
|
4040
|
+
loadFromDisk();
|
|
4041
|
+
await fetchFromServer();
|
|
4042
|
+
if (!url) return;
|
|
4043
|
+
if (refreshHours > 0) {
|
|
4044
|
+
const ms = refreshHours * 60 * 60 * 1e3;
|
|
4045
|
+
refreshTimer = setInterval(() => fetchFromServer().catch(() => {
|
|
4046
|
+
}), ms);
|
|
4047
|
+
}
|
|
4048
|
+
stopWatching = watchCredentials(() => fetchFromServer().catch(() => {
|
|
4049
|
+
}));
|
|
4050
|
+
},
|
|
4051
|
+
stop() {
|
|
4052
|
+
stopWatching?.();
|
|
4053
|
+
stopWatching = null;
|
|
4054
|
+
if (refreshTimer) {
|
|
4055
|
+
clearInterval(refreshTimer);
|
|
4056
|
+
refreshTimer = null;
|
|
4057
|
+
}
|
|
4058
|
+
map.clear();
|
|
4059
|
+
}
|
|
4060
|
+
};
|
|
4061
|
+
}
|
|
4062
|
+
|
|
3839
4063
|
// src/notification/storage.ts
|
|
3840
4064
|
import {
|
|
3841
4065
|
accessSync,
|
|
3842
|
-
mkdirSync,
|
|
4066
|
+
mkdirSync as mkdirSync4,
|
|
3843
4067
|
appendFileSync,
|
|
3844
4068
|
readdirSync,
|
|
3845
|
-
readFileSync,
|
|
3846
|
-
writeFileSync,
|
|
3847
|
-
existsSync,
|
|
4069
|
+
readFileSync as readFileSync4,
|
|
4070
|
+
writeFileSync as writeFileSync4,
|
|
4071
|
+
existsSync as existsSync4,
|
|
3848
4072
|
rmSync,
|
|
3849
4073
|
constants
|
|
3850
4074
|
} from "fs";
|
|
3851
|
-
import { join } from "path";
|
|
4075
|
+
import { join as join4 } from "path";
|
|
3852
4076
|
var NOTIFICATION_DIR_NAME = "notifications";
|
|
3853
4077
|
var ID_INDEX_DIR_NAME = ".ids";
|
|
3854
4078
|
function getStateFallbackNotificationDir(stateDir) {
|
|
3855
|
-
return
|
|
4079
|
+
return join4(stateDir, "plugins", "phone-notifications", NOTIFICATION_DIR_NAME);
|
|
3856
4080
|
}
|
|
3857
4081
|
function ensureWritableDirectory(dir) {
|
|
3858
4082
|
try {
|
|
3859
|
-
|
|
4083
|
+
mkdirSync4(dir, { recursive: true });
|
|
3860
4084
|
accessSync(dir, constants.R_OK | constants.W_OK);
|
|
3861
4085
|
return true;
|
|
3862
4086
|
} catch {
|
|
@@ -3870,7 +4094,7 @@ function resolveNotificationStorageDir(ctx, logger) {
|
|
|
3870
4094
|
return stateNotifDir;
|
|
3871
4095
|
}
|
|
3872
4096
|
if (ctx.workspaceDir) {
|
|
3873
|
-
const workspaceDir =
|
|
4097
|
+
const workspaceDir = join4(ctx.workspaceDir, NOTIFICATION_DIR_NAME);
|
|
3874
4098
|
if (ensureWritableDirectory(workspaceDir)) {
|
|
3875
4099
|
logger.warn(
|
|
3876
4100
|
`stateDir \u4E0D\u53EF\u7528\uFF0C\u901A\u77E5\u5DF2\u56DE\u9000\u5230 workspace \u8DEF\u5F84: ${workspaceDir}`
|
|
@@ -3881,53 +4105,58 @@ function resolveNotificationStorageDir(ctx, logger) {
|
|
|
3881
4105
|
throw new Error(`\u901A\u77E5\u5B58\u50A8\u76EE\u5F55\u4E0D\u53EF\u7528: ${stateNotifDir}`);
|
|
3882
4106
|
}
|
|
3883
4107
|
var NotificationStorage = class {
|
|
3884
|
-
constructor(dir, config, logger) {
|
|
4108
|
+
constructor(dir, config, logger, resolveDisplayName) {
|
|
3885
4109
|
this.config = config;
|
|
3886
4110
|
this.logger = logger;
|
|
3887
4111
|
this.dir = dir;
|
|
3888
|
-
this.idIndexDir =
|
|
4112
|
+
this.idIndexDir = join4(dir, ID_INDEX_DIR_NAME);
|
|
4113
|
+
this.resolveDisplayName = resolveDisplayName;
|
|
3889
4114
|
}
|
|
3890
4115
|
dir;
|
|
3891
4116
|
idIndexDir;
|
|
3892
4117
|
idCache = /* @__PURE__ */ new Map();
|
|
4118
|
+
resolveDisplayName;
|
|
3893
4119
|
async init() {
|
|
3894
|
-
|
|
3895
|
-
|
|
4120
|
+
mkdirSync4(this.dir, { recursive: true });
|
|
4121
|
+
mkdirSync4(this.idIndexDir, { recursive: true });
|
|
3896
4122
|
}
|
|
3897
4123
|
async ingest(items) {
|
|
3898
4124
|
for (const n of items) {
|
|
3899
|
-
this.writeNotification(n);
|
|
4125
|
+
await this.writeNotification(n);
|
|
3900
4126
|
}
|
|
3901
4127
|
this.prune();
|
|
3902
4128
|
}
|
|
3903
|
-
writeNotification(n) {
|
|
4129
|
+
async writeNotification(n) {
|
|
3904
4130
|
const ts = new Date(n.timestamp);
|
|
3905
4131
|
if (Number.isNaN(ts.getTime())) {
|
|
3906
4132
|
this.logger.warn(`\u5FFD\u7565\u975E\u6CD5 timestamp \u7684\u901A\u77E5: ${n.id}`);
|
|
3907
4133
|
return;
|
|
3908
4134
|
}
|
|
3909
4135
|
const dateKey = this.formatDate(ts);
|
|
3910
|
-
const filePath =
|
|
4136
|
+
const filePath = join4(this.dir, `${dateKey}.json`);
|
|
3911
4137
|
const normalizedId = typeof n.id === "string" ? n.id.trim() : "";
|
|
3912
4138
|
if (normalizedId && this.hasNotificationId(dateKey, normalizedId)) {
|
|
3913
4139
|
return;
|
|
3914
4140
|
}
|
|
4141
|
+
const appName = typeof n.app === "string" && n.app ? n.app : "Unknown";
|
|
4142
|
+
const appDisplayName = this.resolveDisplayName ? await this.resolveDisplayName(appName) : appName;
|
|
3915
4143
|
const entry = {
|
|
3916
|
-
appName
|
|
4144
|
+
appName,
|
|
4145
|
+
appDisplayName,
|
|
3917
4146
|
title: typeof n.title === "string" ? n.title : "",
|
|
3918
4147
|
content: this.buildContent(n),
|
|
3919
4148
|
timestamp: n.timestamp
|
|
3920
4149
|
};
|
|
3921
4150
|
let arr = [];
|
|
3922
|
-
if (
|
|
4151
|
+
if (existsSync4(filePath)) {
|
|
3923
4152
|
try {
|
|
3924
|
-
arr = JSON.parse(
|
|
4153
|
+
arr = JSON.parse(readFileSync4(filePath, "utf-8"));
|
|
3925
4154
|
} catch {
|
|
3926
4155
|
arr = [];
|
|
3927
4156
|
}
|
|
3928
4157
|
}
|
|
3929
4158
|
arr.push(entry);
|
|
3930
|
-
|
|
4159
|
+
writeFileSync4(filePath, JSON.stringify(arr, null, 2), "utf-8");
|
|
3931
4160
|
if (normalizedId) {
|
|
3932
4161
|
this.recordNotificationId(dateKey, normalizedId);
|
|
3933
4162
|
}
|
|
@@ -3953,7 +4182,7 @@ var NotificationStorage = class {
|
|
|
3953
4182
|
return `${year}-${month}-${day}`;
|
|
3954
4183
|
}
|
|
3955
4184
|
getIdIndexPath(dateKey) {
|
|
3956
|
-
return
|
|
4185
|
+
return join4(this.idIndexDir, `${dateKey}.ids`);
|
|
3957
4186
|
}
|
|
3958
4187
|
getIdSet(dateKey) {
|
|
3959
4188
|
const cached = this.idCache.get(dateKey);
|
|
@@ -3962,8 +4191,8 @@ var NotificationStorage = class {
|
|
|
3962
4191
|
}
|
|
3963
4192
|
const idPath = this.getIdIndexPath(dateKey);
|
|
3964
4193
|
const ids = /* @__PURE__ */ new Set();
|
|
3965
|
-
if (
|
|
3966
|
-
const lines =
|
|
4194
|
+
if (existsSync4(idPath)) {
|
|
4195
|
+
const lines = readFileSync4(idPath, "utf-8").split(/\r?\n/);
|
|
3967
4196
|
for (const line of lines) {
|
|
3968
4197
|
const id = line.trim();
|
|
3969
4198
|
if (id) {
|
|
@@ -4002,10 +4231,10 @@ var NotificationStorage = class {
|
|
|
4002
4231
|
if (entry.isFile()) {
|
|
4003
4232
|
const match = dateFilePattern.exec(entry.name);
|
|
4004
4233
|
if (match && match[1] < cutoffDate) {
|
|
4005
|
-
rmSync(
|
|
4234
|
+
rmSync(join4(this.dir, entry.name), { force: true });
|
|
4006
4235
|
}
|
|
4007
4236
|
} else if (entry.isDirectory() && dateDirPattern.test(entry.name) && entry.name < cutoffDate) {
|
|
4008
|
-
rmSync(
|
|
4237
|
+
rmSync(join4(this.dir, entry.name), { recursive: true, force: true });
|
|
4009
4238
|
}
|
|
4010
4239
|
}
|
|
4011
4240
|
} catch {
|
|
@@ -4018,7 +4247,7 @@ var NotificationStorage = class {
|
|
|
4018
4247
|
if (!entry.isFile()) continue;
|
|
4019
4248
|
const match = /^(\d{4}-\d{2}-\d{2})\.ids$/.exec(entry.name);
|
|
4020
4249
|
if (match && match[1] < cutoffDate) {
|
|
4021
|
-
rmSync(
|
|
4250
|
+
rmSync(join4(this.idIndexDir, entry.name), { force: true });
|
|
4022
4251
|
this.idCache.delete(match[1]);
|
|
4023
4252
|
}
|
|
4024
4253
|
}
|
|
@@ -4031,24 +4260,24 @@ var NotificationStorage = class {
|
|
|
4031
4260
|
};
|
|
4032
4261
|
|
|
4033
4262
|
// src/cli/auth.ts
|
|
4034
|
-
import { existsSync as
|
|
4263
|
+
import { existsSync as existsSync6, rmSync as rmSync2 } from "fs";
|
|
4035
4264
|
|
|
4036
4265
|
// src/cli/helpers.ts
|
|
4037
|
-
import { existsSync as
|
|
4038
|
-
import { join as
|
|
4266
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync as readdirSync2 } from "fs";
|
|
4267
|
+
import { join as join5 } from "path";
|
|
4039
4268
|
function resolveNotificationsDir(ctx) {
|
|
4040
4269
|
if (ctx.stateDir) {
|
|
4041
|
-
const dir =
|
|
4270
|
+
const dir = join5(
|
|
4042
4271
|
ctx.stateDir,
|
|
4043
4272
|
"plugins",
|
|
4044
4273
|
"phone-notifications",
|
|
4045
4274
|
"notifications"
|
|
4046
4275
|
);
|
|
4047
|
-
if (
|
|
4276
|
+
if (existsSync5(dir)) return dir;
|
|
4048
4277
|
}
|
|
4049
4278
|
if (ctx.workspaceDir) {
|
|
4050
|
-
const dir =
|
|
4051
|
-
if (
|
|
4279
|
+
const dir = join5(ctx.workspaceDir, "notifications");
|
|
4280
|
+
if (existsSync5(dir)) return dir;
|
|
4052
4281
|
}
|
|
4053
4282
|
return null;
|
|
4054
4283
|
}
|
|
@@ -4063,10 +4292,10 @@ function listDateKeys(dir) {
|
|
|
4063
4292
|
return keys.sort().reverse();
|
|
4064
4293
|
}
|
|
4065
4294
|
function readDateFile(dir, dateKey) {
|
|
4066
|
-
const filePath =
|
|
4067
|
-
if (!
|
|
4295
|
+
const filePath = join5(dir, `${dateKey}.json`);
|
|
4296
|
+
if (!existsSync5(filePath)) return [];
|
|
4068
4297
|
try {
|
|
4069
|
-
return JSON.parse(
|
|
4298
|
+
return JSON.parse(readFileSync5(filePath, "utf-8"));
|
|
4070
4299
|
} catch {
|
|
4071
4300
|
return [];
|
|
4072
4301
|
}
|
|
@@ -4096,48 +4325,6 @@ function exitError(code, message) {
|
|
|
4096
4325
|
process.exit(1);
|
|
4097
4326
|
}
|
|
4098
4327
|
|
|
4099
|
-
// src/auth/credentials.ts
|
|
4100
|
-
import {
|
|
4101
|
-
existsSync as existsSync3,
|
|
4102
|
-
mkdirSync as mkdirSync2,
|
|
4103
|
-
readFileSync as readFileSync3,
|
|
4104
|
-
writeFileSync as writeFileSync2
|
|
4105
|
-
} from "fs";
|
|
4106
|
-
import { join as join3, dirname } from "path";
|
|
4107
|
-
function credentialsPath() {
|
|
4108
|
-
const home = process.env.HOME || process.env.USERPROFILE || "/tmp";
|
|
4109
|
-
return join3(home, ".openclaw", "credentials.json");
|
|
4110
|
-
}
|
|
4111
|
-
function readCredentials() {
|
|
4112
|
-
const path2 = credentialsPath();
|
|
4113
|
-
if (!existsSync3(path2)) return {};
|
|
4114
|
-
try {
|
|
4115
|
-
return JSON.parse(readFileSync3(path2, "utf-8"));
|
|
4116
|
-
} catch {
|
|
4117
|
-
return {};
|
|
4118
|
-
}
|
|
4119
|
-
}
|
|
4120
|
-
function writeCredentials(creds) {
|
|
4121
|
-
const path2 = credentialsPath();
|
|
4122
|
-
mkdirSync2(dirname(path2), { recursive: true, mode: 448 });
|
|
4123
|
-
writeFileSync2(path2, JSON.stringify(creds, null, 2), {
|
|
4124
|
-
encoding: "utf-8",
|
|
4125
|
-
mode: 384
|
|
4126
|
-
});
|
|
4127
|
-
}
|
|
4128
|
-
function loadToken() {
|
|
4129
|
-
return readCredentials().token;
|
|
4130
|
-
}
|
|
4131
|
-
function requireToken() {
|
|
4132
|
-
const token = loadToken();
|
|
4133
|
-
if (!token) {
|
|
4134
|
-
throw new Error(
|
|
4135
|
-
"Token \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u5148\u6267\u884C openclaw ntf auth set-token <token>\uFF08\u82E5 ntf \u547D\u4EE4\u51B2\u7A81\uFF0C\u53EF\u4F7F\u7528 openclaw phone-notifications auth set-token <token>\uFF09"
|
|
4136
|
-
);
|
|
4137
|
-
}
|
|
4138
|
-
return token;
|
|
4139
|
-
}
|
|
4140
|
-
|
|
4141
4328
|
// src/cli/auth.ts
|
|
4142
4329
|
function registerAuthCli(program) {
|
|
4143
4330
|
const auth = program.command("auth").description("\u7528\u6237\u8BA4\u8BC1\u7BA1\u7406");
|
|
@@ -4164,7 +4351,7 @@ function registerAuthCli(program) {
|
|
|
4164
4351
|
});
|
|
4165
4352
|
auth.command("clear").description("\u6E05\u9664\u5DF2\u4FDD\u5B58\u7684\u8BA4\u8BC1\u4FE1\u606F").action(() => {
|
|
4166
4353
|
const path2 = credentialsPath();
|
|
4167
|
-
if (
|
|
4354
|
+
if (existsSync6(path2)) {
|
|
4168
4355
|
const creds = readCredentials();
|
|
4169
4356
|
delete creds.token;
|
|
4170
4357
|
if (Object.keys(creds).length === 0) {
|
|
@@ -4263,22 +4450,22 @@ function registerNtfStats(ntf, ctx) {
|
|
|
4263
4450
|
}
|
|
4264
4451
|
|
|
4265
4452
|
// src/cli/ntf-sync.ts
|
|
4266
|
-
import { existsSync as
|
|
4267
|
-
import { join as
|
|
4453
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
4454
|
+
import { join as join6 } from "path";
|
|
4268
4455
|
function checkpointPath(dir) {
|
|
4269
|
-
return
|
|
4456
|
+
return join6(dir, ".checkpoint.json");
|
|
4270
4457
|
}
|
|
4271
4458
|
function readCheckpoint(dir) {
|
|
4272
4459
|
const p = checkpointPath(dir);
|
|
4273
|
-
if (!
|
|
4460
|
+
if (!existsSync7(p)) return {};
|
|
4274
4461
|
try {
|
|
4275
|
-
return JSON.parse(
|
|
4462
|
+
return JSON.parse(readFileSync6(p, "utf-8"));
|
|
4276
4463
|
} catch {
|
|
4277
4464
|
return {};
|
|
4278
4465
|
}
|
|
4279
4466
|
}
|
|
4280
4467
|
function writeCheckpoint(dir, data) {
|
|
4281
|
-
|
|
4468
|
+
writeFileSync5(checkpointPath(dir), JSON.stringify(data, null, 2), "utf-8");
|
|
4282
4469
|
}
|
|
4283
4470
|
function registerNtfSync(ntf, ctx) {
|
|
4284
4471
|
const sync = ntf.command("sync").description("\u540C\u6B65\u901A\u77E5\u5230\u8BB0\u5FC6\u7CFB\u7EDF");
|
|
@@ -4352,30 +4539,30 @@ function registerNtfSync(ntf, ctx) {
|
|
|
4352
4539
|
|
|
4353
4540
|
// src/cli/ntf-monitor.ts
|
|
4354
4541
|
import {
|
|
4355
|
-
existsSync as
|
|
4356
|
-
mkdirSync as
|
|
4357
|
-
readFileSync as
|
|
4358
|
-
writeFileSync as
|
|
4542
|
+
existsSync as existsSync8,
|
|
4543
|
+
mkdirSync as mkdirSync5,
|
|
4544
|
+
readFileSync as readFileSync7,
|
|
4545
|
+
writeFileSync as writeFileSync6,
|
|
4359
4546
|
rmSync as rmSync3,
|
|
4360
4547
|
readdirSync as readdirSync3
|
|
4361
4548
|
} from "fs";
|
|
4362
|
-
import { join as
|
|
4549
|
+
import { join as join7 } from "path";
|
|
4363
4550
|
function tasksDir(ctx) {
|
|
4364
4551
|
const base = ctx.workspaceDir || ctx.stateDir;
|
|
4365
4552
|
if (!base) throw new Error("workspaceDir and stateDir both unavailable");
|
|
4366
|
-
return
|
|
4553
|
+
return join7(base, "tasks");
|
|
4367
4554
|
}
|
|
4368
4555
|
function readMeta(taskDir) {
|
|
4369
|
-
const metaPath =
|
|
4370
|
-
if (!
|
|
4556
|
+
const metaPath = join7(taskDir, "meta.json");
|
|
4557
|
+
if (!existsSync8(metaPath)) return null;
|
|
4371
4558
|
try {
|
|
4372
|
-
return JSON.parse(
|
|
4559
|
+
return JSON.parse(readFileSync7(metaPath, "utf-8"));
|
|
4373
4560
|
} catch {
|
|
4374
4561
|
return null;
|
|
4375
4562
|
}
|
|
4376
4563
|
}
|
|
4377
4564
|
function writeMeta(taskDir, meta) {
|
|
4378
|
-
|
|
4565
|
+
writeFileSync6(join7(taskDir, "meta.json"), JSON.stringify(meta, null, 2), "utf-8");
|
|
4379
4566
|
}
|
|
4380
4567
|
function generateFetchPy(name, matchRules) {
|
|
4381
4568
|
const appName = matchRules.appName || "";
|
|
@@ -4452,27 +4639,27 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4452
4639
|
const monitor = ntf.command("monitor").description("\u901A\u77E5\u76D1\u63A7\u4EFB\u52A1\u7BA1\u7406");
|
|
4453
4640
|
monitor.command("list").description("\u5217\u51FA\u6240\u6709\u76D1\u63A7\u4EFB\u52A1").action(() => {
|
|
4454
4641
|
const dir = tasksDir(ctx);
|
|
4455
|
-
if (!
|
|
4642
|
+
if (!existsSync8(dir)) {
|
|
4456
4643
|
output({ ok: true, tasks: [] });
|
|
4457
4644
|
return;
|
|
4458
4645
|
}
|
|
4459
4646
|
const tasks = [];
|
|
4460
4647
|
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
4461
4648
|
if (!entry.isDirectory()) continue;
|
|
4462
|
-
const meta = readMeta(
|
|
4649
|
+
const meta = readMeta(join7(dir, entry.name));
|
|
4463
4650
|
if (meta) tasks.push(meta);
|
|
4464
4651
|
}
|
|
4465
4652
|
output({ ok: true, tasks });
|
|
4466
4653
|
});
|
|
4467
4654
|
monitor.command("show <name>").description("\u67E5\u770B\u76D1\u63A7\u4EFB\u52A1\u8BE6\u60C5").action((name) => {
|
|
4468
|
-
const taskDir =
|
|
4655
|
+
const taskDir = join7(tasksDir(ctx), name);
|
|
4469
4656
|
const meta = readMeta(taskDir);
|
|
4470
4657
|
if (!meta) exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
|
|
4471
|
-
const checkpointPath2 =
|
|
4658
|
+
const checkpointPath2 = join7(taskDir, "checkpoint.json");
|
|
4472
4659
|
let checkpoint = {};
|
|
4473
|
-
if (
|
|
4660
|
+
if (existsSync8(checkpointPath2)) {
|
|
4474
4661
|
try {
|
|
4475
|
-
checkpoint = JSON.parse(
|
|
4662
|
+
checkpoint = JSON.parse(readFileSync7(checkpointPath2, "utf-8"));
|
|
4476
4663
|
} catch {
|
|
4477
4664
|
}
|
|
4478
4665
|
}
|
|
@@ -4487,8 +4674,8 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4487
4674
|
monitor.command("create <name>").description("\u521B\u5EFA\u76D1\u63A7\u4EFB\u52A1").requiredOption("--description <text>", "\u4EFB\u52A1\u63CF\u8FF0").requiredOption("--match-rules <json>", "\u5339\u914D\u89C4\u5219 JSON").requiredOption("--schedule <cron>", "cron \u8868\u8FBE\u5F0F").action(
|
|
4488
4675
|
(name, opts) => {
|
|
4489
4676
|
const dir = tasksDir(ctx);
|
|
4490
|
-
const taskDir =
|
|
4491
|
-
if (
|
|
4677
|
+
const taskDir = join7(dir, name);
|
|
4678
|
+
if (existsSync8(taskDir)) {
|
|
4492
4679
|
exitError("ALREADY_EXISTS", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u5DF2\u5B58\u5728`);
|
|
4493
4680
|
}
|
|
4494
4681
|
let matchRules;
|
|
@@ -4500,7 +4687,7 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4500
4687
|
"match-rules \u5FC5\u987B\u662F\u5408\u6CD5\u7684 JSON"
|
|
4501
4688
|
);
|
|
4502
4689
|
}
|
|
4503
|
-
|
|
4690
|
+
mkdirSync5(taskDir, { recursive: true });
|
|
4504
4691
|
const meta = {
|
|
4505
4692
|
name,
|
|
4506
4693
|
description: opts.description,
|
|
@@ -4510,13 +4697,13 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4510
4697
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4511
4698
|
};
|
|
4512
4699
|
writeMeta(taskDir, meta);
|
|
4513
|
-
|
|
4514
|
-
|
|
4700
|
+
writeFileSync6(
|
|
4701
|
+
join7(taskDir, "fetch.py"),
|
|
4515
4702
|
generateFetchPy(name, matchRules),
|
|
4516
4703
|
"utf-8"
|
|
4517
4704
|
);
|
|
4518
|
-
|
|
4519
|
-
|
|
4705
|
+
writeFileSync6(
|
|
4706
|
+
join7(taskDir, "README.md"),
|
|
4520
4707
|
generateReadme(name, opts.description),
|
|
4521
4708
|
"utf-8"
|
|
4522
4709
|
);
|
|
@@ -4544,8 +4731,8 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4544
4731
|
}
|
|
4545
4732
|
);
|
|
4546
4733
|
monitor.command("delete <name>").description("\u5220\u9664\u76D1\u63A7\u4EFB\u52A1").option("--yes", "\u8DF3\u8FC7\u786E\u8BA4").action((name, opts) => {
|
|
4547
|
-
const taskDir =
|
|
4548
|
-
if (!
|
|
4734
|
+
const taskDir = join7(tasksDir(ctx), name);
|
|
4735
|
+
if (!existsSync8(taskDir)) {
|
|
4549
4736
|
exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
|
|
4550
4737
|
}
|
|
4551
4738
|
if (!opts.yes) {
|
|
@@ -4570,7 +4757,7 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4570
4757
|
});
|
|
4571
4758
|
});
|
|
4572
4759
|
monitor.command("enable <name>").description("\u542F\u7528\u76D1\u63A7\u4EFB\u52A1").action((name) => {
|
|
4573
|
-
const taskDir =
|
|
4760
|
+
const taskDir = join7(tasksDir(ctx), name);
|
|
4574
4761
|
const meta = readMeta(taskDir);
|
|
4575
4762
|
if (!meta) exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
|
|
4576
4763
|
meta.enabled = true;
|
|
@@ -4578,7 +4765,7 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4578
4765
|
output({ ok: true, name, enabled: true });
|
|
4579
4766
|
});
|
|
4580
4767
|
monitor.command("disable <name>").description("\u6682\u505C\u76D1\u63A7\u4EFB\u52A1").action((name) => {
|
|
4581
|
-
const taskDir =
|
|
4768
|
+
const taskDir = join7(tasksDir(ctx), name);
|
|
4582
4769
|
const meta = readMeta(taskDir);
|
|
4583
4770
|
if (!meta) exitError("NOT_FOUND", `\u76D1\u63A7\u4EFB\u52A1 '${name}' \u4E0D\u5B58\u5728`);
|
|
4584
4771
|
meta.enabled = false;
|
|
@@ -4588,8 +4775,8 @@ function registerNtfMonitor(ntf, ctx) {
|
|
|
4588
4775
|
}
|
|
4589
4776
|
|
|
4590
4777
|
// src/cli/light-rules.ts
|
|
4591
|
-
import { existsSync as
|
|
4592
|
-
import { join as
|
|
4778
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
4779
|
+
import { join as join8 } from "path";
|
|
4593
4780
|
|
|
4594
4781
|
// src/light/validators.ts
|
|
4595
4782
|
var VALID_MODES = ["wave", "breath", "strobe", "steady", "wave_rainbow"];
|
|
@@ -4654,15 +4841,15 @@ var LIGHT_RULES_SECTION = "## \u706F\u6548\u89C4\u5219";
|
|
|
4654
4841
|
function memoryPath(ctx) {
|
|
4655
4842
|
const base = ctx.workspaceDir || ctx.stateDir;
|
|
4656
4843
|
if (!base) throw new Error("workspaceDir and stateDir both unavailable");
|
|
4657
|
-
return
|
|
4844
|
+
return join8(base, "MEMORY.md");
|
|
4658
4845
|
}
|
|
4659
4846
|
function readMemory(ctx) {
|
|
4660
4847
|
const p = memoryPath(ctx);
|
|
4661
|
-
if (!
|
|
4662
|
-
return
|
|
4848
|
+
if (!existsSync9(p)) return "";
|
|
4849
|
+
return readFileSync8(p, "utf-8");
|
|
4663
4850
|
}
|
|
4664
4851
|
function writeMemory(ctx, content) {
|
|
4665
|
-
|
|
4852
|
+
writeFileSync7(memoryPath(ctx), content, "utf-8");
|
|
4666
4853
|
}
|
|
4667
4854
|
function parseLightRules(content) {
|
|
4668
4855
|
const rules = [];
|
|
@@ -4841,15 +5028,15 @@ function registerLightSend(light) {
|
|
|
4841
5028
|
}
|
|
4842
5029
|
|
|
4843
5030
|
// src/cli/tunnel-status.ts
|
|
4844
|
-
import { existsSync as
|
|
4845
|
-
import { join as
|
|
4846
|
-
var STATUS_REL_PATH =
|
|
5031
|
+
import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
|
|
5032
|
+
import { join as join9 } from "path";
|
|
5033
|
+
var STATUS_REL_PATH = join9("plugins", "phone-notifications", "tunnel-status.json");
|
|
4847
5034
|
function readTunnelStatus(ctx) {
|
|
4848
5035
|
if (!ctx.stateDir) return null;
|
|
4849
|
-
const filePath =
|
|
4850
|
-
if (!
|
|
5036
|
+
const filePath = join9(ctx.stateDir, STATUS_REL_PATH);
|
|
5037
|
+
if (!existsSync10(filePath)) return null;
|
|
4851
5038
|
try {
|
|
4852
|
-
return JSON.parse(
|
|
5039
|
+
return JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
4853
5040
|
} catch {
|
|
4854
5041
|
return null;
|
|
4855
5042
|
}
|
|
@@ -4870,7 +5057,7 @@ function formatMessage(status) {
|
|
|
4870
5057
|
}
|
|
4871
5058
|
function registerTunnelStatus(ntf, ctx) {
|
|
4872
5059
|
ntf.command("tunnel-status").description("\u68C0\u67E5 Relay Tunnel \u96A7\u9053\u8FDE\u63A5\u72B6\u6001\uFF08\u8BFB\u53D6\u8FD0\u884C\u4E2D\u670D\u52A1\u7684\u72B6\u6001\u6587\u4EF6\uFF0C\u4E0D\u5F71\u54CD\u5DF2\u6709\u8FDE\u63A5\uFF09").action(async () => {
|
|
4873
|
-
const tunnelUrl =
|
|
5060
|
+
const tunnelUrl = getEnvUrls().relayTunnelUrl;
|
|
4874
5061
|
const token = loadToken();
|
|
4875
5062
|
if (!tunnelUrl) {
|
|
4876
5063
|
exitError(
|
|
@@ -4917,17 +5104,17 @@ function registerNtfStoragePath(ntf, ctx) {
|
|
|
4917
5104
|
}
|
|
4918
5105
|
|
|
4919
5106
|
// src/cli/log-search.ts
|
|
4920
|
-
import { existsSync as
|
|
4921
|
-
import { join as
|
|
5107
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10, readdirSync as readdirSync4 } from "fs";
|
|
5108
|
+
import { join as join10 } from "path";
|
|
4922
5109
|
function resolveLogsDir(ctx) {
|
|
4923
5110
|
if (ctx.stateDir) {
|
|
4924
|
-
const dir =
|
|
5111
|
+
const dir = join10(
|
|
4925
5112
|
ctx.stateDir,
|
|
4926
5113
|
"plugins",
|
|
4927
5114
|
"phone-notifications",
|
|
4928
5115
|
"logs"
|
|
4929
5116
|
);
|
|
4930
|
-
if (
|
|
5117
|
+
if (existsSync11(dir)) return dir;
|
|
4931
5118
|
}
|
|
4932
5119
|
return null;
|
|
4933
5120
|
}
|
|
@@ -4942,9 +5129,9 @@ function listLogDateKeys(dir) {
|
|
|
4942
5129
|
return keys.sort().reverse();
|
|
4943
5130
|
}
|
|
4944
5131
|
function collectLogLines(dir, dateKey, keyword, limit, collected) {
|
|
4945
|
-
const filePath =
|
|
4946
|
-
if (!
|
|
4947
|
-
const content =
|
|
5132
|
+
const filePath = join10(dir, `${dateKey}.log`);
|
|
5133
|
+
if (!existsSync11(filePath)) return;
|
|
5134
|
+
const content = readFileSync10(filePath, "utf-8");
|
|
4948
5135
|
const lowerKeyword = keyword?.toLowerCase();
|
|
4949
5136
|
for (const line of content.split("\n")) {
|
|
4950
5137
|
if (collected.length >= limit) return;
|
|
@@ -4975,13 +5162,47 @@ function registerLogSearch(ntf, ctx) {
|
|
|
4975
5162
|
);
|
|
4976
5163
|
}
|
|
4977
5164
|
|
|
5165
|
+
// src/cli/env.ts
|
|
5166
|
+
function registerEnvCli(ntf) {
|
|
5167
|
+
const env = ntf.command("env").description("\u73AF\u5883\u7BA1\u7406\uFF08\u5207\u6362 development / production\uFF09");
|
|
5168
|
+
env.command("show").description("\u663E\u793A\u5F53\u524D\u73AF\u5883\u53CA\u5BF9\u5E94\u7684\u63A5\u53E3\u5730\u5740").action(() => {
|
|
5169
|
+
const current = loadEnvName();
|
|
5170
|
+
const urls = getEnvUrls(current);
|
|
5171
|
+
output({
|
|
5172
|
+
ok: true,
|
|
5173
|
+
env: current,
|
|
5174
|
+
lightApiUrl: urls.lightApiUrl,
|
|
5175
|
+
relayTunnelUrl: urls.relayTunnelUrl,
|
|
5176
|
+
availableEnvs: getAvailableEnvs()
|
|
5177
|
+
});
|
|
5178
|
+
});
|
|
5179
|
+
env.command("switch <env>").description("\u5207\u6362\u73AF\u5883\uFF08development / production\uFF09").action((envName) => {
|
|
5180
|
+
const available = getAvailableEnvs();
|
|
5181
|
+
if (!available.includes(envName)) {
|
|
5182
|
+
exitError(
|
|
5183
|
+
"INVALID_ENV",
|
|
5184
|
+
`\u65E0\u6548\u7684\u73AF\u5883\u540D\u79F0: ${envName}\uFF0C\u53EF\u9009\u503C: ${available.join(", ")}`
|
|
5185
|
+
);
|
|
5186
|
+
}
|
|
5187
|
+
saveEnvName(envName);
|
|
5188
|
+
const urls = getEnvUrls(envName);
|
|
5189
|
+
output({
|
|
5190
|
+
ok: true,
|
|
5191
|
+
message: `\u5DF2\u5207\u6362\u5230 ${envName} \u73AF\u5883`,
|
|
5192
|
+
env: envName,
|
|
5193
|
+
lightApiUrl: urls.lightApiUrl,
|
|
5194
|
+
relayTunnelUrl: urls.relayTunnelUrl
|
|
5195
|
+
});
|
|
5196
|
+
});
|
|
5197
|
+
}
|
|
5198
|
+
|
|
4978
5199
|
// src/version.ts
|
|
4979
|
-
import { readFileSync as
|
|
5200
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
4980
5201
|
function readPluginVersion() {
|
|
4981
5202
|
try {
|
|
4982
5203
|
const packageJsonUrl = new URL("../package.json", import.meta.url);
|
|
4983
5204
|
const packageJson = JSON.parse(
|
|
4984
|
-
|
|
5205
|
+
readFileSync11(packageJsonUrl, "utf-8")
|
|
4985
5206
|
);
|
|
4986
5207
|
return packageJson.version ?? "unknown";
|
|
4987
5208
|
} catch {
|
|
@@ -5003,22 +5224,23 @@ function registerAllCli(program, ctx, rootCommandName = "ntf") {
|
|
|
5003
5224
|
registerNtfStoragePath(ntf, ctx);
|
|
5004
5225
|
registerLogSearch(ntf, ctx);
|
|
5005
5226
|
registerTunnelStatus(ntf, ctx);
|
|
5227
|
+
registerEnvCli(ntf);
|
|
5006
5228
|
}
|
|
5007
5229
|
|
|
5008
5230
|
// src/tunnel/service.ts
|
|
5009
5231
|
import {
|
|
5010
5232
|
closeSync,
|
|
5011
|
-
mkdirSync as
|
|
5233
|
+
mkdirSync as mkdirSync7,
|
|
5012
5234
|
openSync,
|
|
5013
|
-
readFileSync as
|
|
5235
|
+
readFileSync as readFileSync12,
|
|
5014
5236
|
unlinkSync,
|
|
5015
|
-
writeFileSync as
|
|
5237
|
+
writeFileSync as writeFileSync9
|
|
5016
5238
|
} from "fs";
|
|
5017
|
-
import { dirname as
|
|
5239
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
5018
5240
|
|
|
5019
5241
|
// src/tunnel/relay-client.ts
|
|
5020
|
-
import { writeFileSync as
|
|
5021
|
-
import { dirname as
|
|
5242
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
|
|
5243
|
+
import { dirname as dirname3 } from "path";
|
|
5022
5244
|
|
|
5023
5245
|
// node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
|
|
5024
5246
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -5050,8 +5272,8 @@ var RelayClient = class {
|
|
|
5050
5272
|
lastDisconnectReason
|
|
5051
5273
|
};
|
|
5052
5274
|
try {
|
|
5053
|
-
|
|
5054
|
-
|
|
5275
|
+
mkdirSync6(dirname3(this.opts.statusFilePath), { recursive: true });
|
|
5276
|
+
writeFileSync8(this.opts.statusFilePath, JSON.stringify(info, null, 2));
|
|
5055
5277
|
} catch {
|
|
5056
5278
|
}
|
|
5057
5279
|
}
|
|
@@ -5897,7 +6119,7 @@ function createTunnelService(opts) {
|
|
|
5897
6119
|
}
|
|
5898
6120
|
function readLockOwner(filePath) {
|
|
5899
6121
|
try {
|
|
5900
|
-
const parsed = JSON.parse(
|
|
6122
|
+
const parsed = JSON.parse(readFileSync12(filePath, "utf-8"));
|
|
5901
6123
|
return typeof parsed.pid === "number" ? parsed.pid : null;
|
|
5902
6124
|
} catch {
|
|
5903
6125
|
return null;
|
|
@@ -5922,11 +6144,11 @@ function createTunnelService(opts) {
|
|
|
5922
6144
|
}
|
|
5923
6145
|
}
|
|
5924
6146
|
function acquireLock(filePath) {
|
|
5925
|
-
|
|
6147
|
+
mkdirSync7(dirname4(filePath), { recursive: true });
|
|
5926
6148
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
5927
6149
|
try {
|
|
5928
6150
|
const fd = openSync(filePath, "wx", 384);
|
|
5929
|
-
|
|
6151
|
+
writeFileSync9(
|
|
5930
6152
|
fd,
|
|
5931
6153
|
JSON.stringify({
|
|
5932
6154
|
pid: process.pid,
|
|
@@ -5983,12 +6205,12 @@ function createTunnelService(opts) {
|
|
|
5983
6205
|
return;
|
|
5984
6206
|
}
|
|
5985
6207
|
const { logger } = opts;
|
|
5986
|
-
const baseStateDir =
|
|
6208
|
+
const baseStateDir = join11(ctx.stateDir, "plugins", "phone-notifications");
|
|
5987
6209
|
logger.info(
|
|
5988
6210
|
`Relay tunnel: starting (pid=${process.pid}, url=${opts.tunnelUrl}, heartbeat=${opts.heartbeatSec ?? DEFAULT_HEARTBEAT_SEC}s, backoff=${opts.reconnectBackoffMs ?? DEFAULT_RECONNECT_BACKOFF_MS}ms, gateway=${opts.gatewayBaseUrl}, hasGatewayToken=${!!opts.gatewayToken}, hasGatewayPwd=${!!opts.gatewayPassword})`
|
|
5989
6211
|
);
|
|
5990
|
-
const statusFilePath =
|
|
5991
|
-
const lockPath =
|
|
6212
|
+
const statusFilePath = join11(baseStateDir, "tunnel-status.json");
|
|
6213
|
+
const lockPath = join11(baseStateDir, "relay-tunnel.lock");
|
|
5992
6214
|
if (!acquireLock(lockPath)) {
|
|
5993
6215
|
return;
|
|
5994
6216
|
}
|
|
@@ -6036,13 +6258,13 @@ function createTunnelService(opts) {
|
|
|
6036
6258
|
}
|
|
6037
6259
|
|
|
6038
6260
|
// src/logger.ts
|
|
6039
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
6040
|
-
import { join as
|
|
6261
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync8 } from "fs";
|
|
6262
|
+
import { join as join12 } from "path";
|
|
6041
6263
|
var PluginFileLogger = class {
|
|
6042
6264
|
constructor(upstream, stateDir) {
|
|
6043
6265
|
this.upstream = upstream;
|
|
6044
|
-
this.logsDir =
|
|
6045
|
-
|
|
6266
|
+
this.logsDir = join12(stateDir, "plugins", "phone-notifications", "logs");
|
|
6267
|
+
mkdirSync8(this.logsDir, { recursive: true });
|
|
6046
6268
|
}
|
|
6047
6269
|
logsDir;
|
|
6048
6270
|
info(msg) {
|
|
@@ -6064,7 +6286,7 @@ var PluginFileLogger = class {
|
|
|
6064
6286
|
const line = `${time} [${level}] ${msg}
|
|
6065
6287
|
`;
|
|
6066
6288
|
try {
|
|
6067
|
-
appendFileSync2(
|
|
6289
|
+
appendFileSync2(join12(this.logsDir, `${dateKey}.log`), line);
|
|
6068
6290
|
} catch {
|
|
6069
6291
|
}
|
|
6070
6292
|
}
|
|
@@ -6101,7 +6323,7 @@ function resolveLocalGatewayAuth(params) {
|
|
|
6101
6323
|
const configPath = params.stateDir ? `${params.stateDir}/openclaw.json` : void 0;
|
|
6102
6324
|
if (configPath) {
|
|
6103
6325
|
try {
|
|
6104
|
-
const configData = JSON.parse(
|
|
6326
|
+
const configData = JSON.parse(readFileSync13(configPath, "utf-8"));
|
|
6105
6327
|
const rawGatewayAuthMode = trimToUndefined(configData?.gateway?.auth?.mode);
|
|
6106
6328
|
if (rawGatewayAuthMode === "token" || rawGatewayAuthMode === "password") {
|
|
6107
6329
|
configGatewayAuthMode = rawGatewayAuthMode;
|
|
@@ -6128,6 +6350,7 @@ var index_default = {
|
|
|
6128
6350
|
register(api) {
|
|
6129
6351
|
const config = api.pluginConfig ?? {};
|
|
6130
6352
|
let storage = null;
|
|
6353
|
+
let appNameMapProvider = null;
|
|
6131
6354
|
const ignoredApps = new Set(config.ignoredApps ?? []);
|
|
6132
6355
|
const openclawDir = api.runtime.state.resolveStateDir();
|
|
6133
6356
|
const logger = openclawDir ? new PluginFileLogger(api.logger, openclawDir) : api.logger;
|
|
@@ -6138,17 +6361,25 @@ var index_default = {
|
|
|
6138
6361
|
id: "notification-storage",
|
|
6139
6362
|
async start(ctx) {
|
|
6140
6363
|
const storageDir = resolveNotificationStorageDir(ctx, logger);
|
|
6141
|
-
|
|
6364
|
+
appNameMapProvider = createAppNameMapProvider({
|
|
6365
|
+
stateDir: ctx.stateDir,
|
|
6366
|
+
logger
|
|
6367
|
+
});
|
|
6368
|
+
await appNameMapProvider.start();
|
|
6369
|
+
const resolveDisplayName = appNameMapProvider.resolveDisplayName.bind(appNameMapProvider);
|
|
6370
|
+
storage = new NotificationStorage(storageDir, config, logger, resolveDisplayName);
|
|
6142
6371
|
await storage.init();
|
|
6143
6372
|
logger.info(`\u901A\u77E5\u5B58\u50A8\u670D\u52A1\u5DF2\u542F\u52A8: ${storageDir}`);
|
|
6144
6373
|
},
|
|
6145
6374
|
async stop() {
|
|
6146
6375
|
await storage?.close();
|
|
6147
6376
|
storage = null;
|
|
6377
|
+
appNameMapProvider?.stop();
|
|
6378
|
+
appNameMapProvider = null;
|
|
6148
6379
|
logger.info("\u901A\u77E5\u5B58\u50A8\u670D\u52A1\u5DF2\u505C\u6B62");
|
|
6149
6380
|
}
|
|
6150
6381
|
});
|
|
6151
|
-
const tunnelUrl =
|
|
6382
|
+
const tunnelUrl = getEnvUrls().relayTunnelUrl;
|
|
6152
6383
|
if (tunnelUrl) {
|
|
6153
6384
|
const gatewayPort = process.env.OPENCLAW_GATEWAY_PORT || "18789";
|
|
6154
6385
|
const { gatewayAuthMode, gatewayToken, gatewayPassword } = resolveLocalGatewayAuth({
|
|
@@ -6373,8 +6604,8 @@ var index_default = {
|
|
|
6373
6604
|
const inferOpenClawRootDir = (workspaceDir) => {
|
|
6374
6605
|
if (openclawDir) return openclawDir;
|
|
6375
6606
|
if (!workspaceDir) return void 0;
|
|
6376
|
-
if (
|
|
6377
|
-
return
|
|
6607
|
+
if (basename2(workspaceDir) !== "workspace") return void 0;
|
|
6608
|
+
return dirname5(workspaceDir);
|
|
6378
6609
|
};
|
|
6379
6610
|
api.registerCli(
|
|
6380
6611
|
(ctx) => {
|