tickflow-assist 0.2.11 → 0.2.13
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 +4 -1
- package/dist/bootstrap.js +1 -1
- package/dist/dev/tickflow-assist-cli.js +165 -0
- package/dist/plugin-commands.js +1 -1
- package/dist/tools/test-alert.tool.d.ts +2 -1
- package/dist/tools/test-alert.tool.js +92 -12
- package/openclaw.plugin.json +5 -6
- package/package.json +2 -2
- package/skills/usage-help/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
基于 [OpenClaw](https://openclaw.ai) 的 A 股监控与分析插件。它使用 [TickFlow](https://tickflow.org/auth/register?ref=BUJ54JEDGE) 获取行情与财务数据,结合 LLM 生成技术面、基本面、资讯面的综合判断,并把结果持久化到本地 LanceDB。
|
|
4
4
|
|
|
5
|
-
最近更新:`v0.2.
|
|
5
|
+
最近更新:`v0.2.13` 在 `configure-openclaw` 与一键安装脚本中加入 Linux 中文字体自动安装,减少 VPS 上 PNG 告警卡中文乱码;并补充 GitHub README 效果预览图。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
@@ -13,6 +13,9 @@ openclaw plugins install tickflow-assist
|
|
|
13
13
|
npx -y tickflow-assist configure-openclaw
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
安装阶段允许先落插件,再通过第二条命令写入 `tickflowApiKey`、`llmApiKey` 等正式配置。
|
|
17
|
+
在 Linux 上,`configure-openclaw` 还会 best-effort 安装 PNG 告警卡所需的中文字体;如需跳过,可追加 `--no-font-setup`。
|
|
18
|
+
|
|
16
19
|
第二条命令会写入 `~/.openclaw/openclaw.json` 中的 `plugins.entries["tickflow-assist"].config`,并默认执行:
|
|
17
20
|
|
|
18
21
|
- `openclaw plugins enable tickflow-assist`
|
package/dist/bootstrap.js
CHANGED
|
@@ -152,7 +152,7 @@ export function createAppContext(config, options = {}) {
|
|
|
152
152
|
startMonitorTool(monitorService, runtime),
|
|
153
153
|
stopDailyUpdateTool(dailyUpdateWorker, runtime),
|
|
154
154
|
stopMonitorTool(monitorService, runtime),
|
|
155
|
-
testAlertTool(alertService),
|
|
155
|
+
testAlertTool(alertService, alertMediaService),
|
|
156
156
|
updateAllTool(dailyUpdateWorker),
|
|
157
157
|
viewAnalysisTool(analysisViewService),
|
|
158
158
|
],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
3
4
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
5
|
import os from "node:os";
|
|
5
6
|
import path from "node:path";
|
|
@@ -38,6 +39,7 @@ Options:
|
|
|
38
39
|
--no-enable Do not run 'openclaw plugins enable'
|
|
39
40
|
--no-restart Do not run 'openclaw gateway restart'
|
|
40
41
|
--no-python-setup Do not run 'uv sync' for Python dependencies
|
|
42
|
+
--no-font-setup Do not try to install Linux Chinese fonts for PNG alerts
|
|
41
43
|
--openclaw-bin <path> OpenClaw CLI binary, default: openclaw
|
|
42
44
|
--tickflow-api-key <key>
|
|
43
45
|
--tickflow-api-key-level <Free|Start|Pro|Expert>
|
|
@@ -61,6 +63,7 @@ function parseArgs(argv) {
|
|
|
61
63
|
restart: true,
|
|
62
64
|
enable: true,
|
|
63
65
|
pythonSetup: true,
|
|
66
|
+
fontSetup: true,
|
|
64
67
|
openclawBin: DEFAULTS.openclawCliBin,
|
|
65
68
|
overrides: {},
|
|
66
69
|
};
|
|
@@ -115,6 +118,9 @@ function parseArgs(argv) {
|
|
|
115
118
|
case "--no-python-setup":
|
|
116
119
|
options.pythonSetup = false;
|
|
117
120
|
break;
|
|
121
|
+
case "--no-font-setup":
|
|
122
|
+
options.fontSetup = false;
|
|
123
|
+
break;
|
|
118
124
|
case "--no-enable":
|
|
119
125
|
options.enable = false;
|
|
120
126
|
break;
|
|
@@ -623,6 +629,162 @@ async function setupPythonDeps(pythonWorkdir, nonInteractive) {
|
|
|
623
629
|
}
|
|
624
630
|
console.log("Python dependencies installed successfully.");
|
|
625
631
|
}
|
|
632
|
+
function isCommandAvailable(command) {
|
|
633
|
+
const result = spawnSync("which", [command], { stdio: "ignore" });
|
|
634
|
+
return result.status === 0;
|
|
635
|
+
}
|
|
636
|
+
function isRootUser() {
|
|
637
|
+
return typeof process.getuid === "function" && process.getuid() === 0;
|
|
638
|
+
}
|
|
639
|
+
function hasChineseFonts() {
|
|
640
|
+
const result = spawnSync("fc-list", [":lang=zh", "family"], { encoding: "utf-8" });
|
|
641
|
+
return result.status === 0 && Boolean(result.stdout.trim());
|
|
642
|
+
}
|
|
643
|
+
function detectLinuxDistro() {
|
|
644
|
+
try {
|
|
645
|
+
const raw = readFileSync("/etc/os-release", "utf-8");
|
|
646
|
+
const record = Object.fromEntries(raw
|
|
647
|
+
.split("\n")
|
|
648
|
+
.map((line) => line.trim())
|
|
649
|
+
.filter(Boolean)
|
|
650
|
+
.filter((line) => !line.startsWith("#") && line.includes("="))
|
|
651
|
+
.map((line) => {
|
|
652
|
+
const index = line.indexOf("=");
|
|
653
|
+
const key = line.slice(0, index);
|
|
654
|
+
const value = line.slice(index + 1).replace(/^"/, "").replace(/"$/, "");
|
|
655
|
+
return [key, value];
|
|
656
|
+
}));
|
|
657
|
+
const ids = [
|
|
658
|
+
String(record.ID ?? "").toLowerCase(),
|
|
659
|
+
...String(record.ID_LIKE ?? "")
|
|
660
|
+
.toLowerCase()
|
|
661
|
+
.split(/\s+/)
|
|
662
|
+
.filter(Boolean),
|
|
663
|
+
];
|
|
664
|
+
if (ids.some((value) => ["debian", "ubuntu"].includes(value))) {
|
|
665
|
+
return "debian";
|
|
666
|
+
}
|
|
667
|
+
if (ids.some((value) => ["rhel", "fedora", "centos", "rocky", "almalinux"].includes(value))) {
|
|
668
|
+
return "rhel";
|
|
669
|
+
}
|
|
670
|
+
if (ids.some((value) => ["arch", "manjaro"].includes(value))) {
|
|
671
|
+
return "arch";
|
|
672
|
+
}
|
|
673
|
+
if (ids.includes("alpine")) {
|
|
674
|
+
return "alpine";
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
catch {
|
|
678
|
+
// fall through
|
|
679
|
+
}
|
|
680
|
+
return "unknown";
|
|
681
|
+
}
|
|
682
|
+
function getManualFontCommands(distro) {
|
|
683
|
+
switch (distro) {
|
|
684
|
+
case "debian":
|
|
685
|
+
return ["sudo apt-get update", "sudo apt-get install -y fontconfig fonts-noto-cjk", "fc-cache -fv"];
|
|
686
|
+
case "rhel":
|
|
687
|
+
return [
|
|
688
|
+
"sudo dnf install -y fontconfig google-noto-sans-cjk-ttc-fonts",
|
|
689
|
+
"fc-cache -fv",
|
|
690
|
+
];
|
|
691
|
+
case "arch":
|
|
692
|
+
return ["sudo pacman -Sy --noconfirm fontconfig noto-fonts-cjk", "fc-cache -fv"];
|
|
693
|
+
case "alpine":
|
|
694
|
+
return ["sudo apk add fontconfig font-noto-cjk", "fc-cache -fv"];
|
|
695
|
+
default:
|
|
696
|
+
return [
|
|
697
|
+
"请安装 fontconfig 和任意可用的中文字体包,例如 Noto Sans CJK",
|
|
698
|
+
"安装后执行: fc-cache -fv",
|
|
699
|
+
];
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function buildPrivilegedCommand(argv, nonInteractive) {
|
|
703
|
+
if (isRootUser()) {
|
|
704
|
+
return argv;
|
|
705
|
+
}
|
|
706
|
+
if (!isCommandAvailable("sudo")) {
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
return nonInteractive ? ["sudo", "-n", ...argv] : ["sudo", ...argv];
|
|
710
|
+
}
|
|
711
|
+
function runSetupCommand(argv, description) {
|
|
712
|
+
console.log(description);
|
|
713
|
+
const result = spawnSync(argv[0], argv.slice(1), { stdio: "inherit" });
|
|
714
|
+
if (result.error) {
|
|
715
|
+
console.warn(`Warning: failed to run ${description}: ${result.error.message}`);
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
if (result.status !== 0) {
|
|
719
|
+
console.warn(`Warning: ${description} exited with status ${result.status}`);
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
async function setupLinuxChineseFonts(nonInteractive) {
|
|
725
|
+
if (process.platform !== "linux") {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
if (!isCommandAvailable("fc-list")) {
|
|
729
|
+
console.warn("Warning: fontconfig is not installed; PNG alerts may not render Chinese text correctly.");
|
|
730
|
+
}
|
|
731
|
+
else if (hasChineseFonts()) {
|
|
732
|
+
console.log("Chinese fonts detected for PNG alert cards.");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
const distro = detectLinuxDistro();
|
|
736
|
+
console.log("Chinese fonts not detected. TickFlow Assist will try to install Noto CJK fonts for PNG alert cards.");
|
|
737
|
+
const attempts = [];
|
|
738
|
+
switch (distro) {
|
|
739
|
+
case "debian":
|
|
740
|
+
attempts.push(["apt-get", "update"]);
|
|
741
|
+
attempts.push(["apt-get", "install", "-y", "fontconfig", "fonts-noto-cjk"]);
|
|
742
|
+
break;
|
|
743
|
+
case "rhel":
|
|
744
|
+
attempts.push(["dnf", "install", "-y", "fontconfig", "google-noto-sans-cjk-ttc-fonts"]);
|
|
745
|
+
attempts.push(["dnf", "install", "-y", "fontconfig", "google-noto-cjk-fonts"]);
|
|
746
|
+
attempts.push(["yum", "install", "-y", "fontconfig", "google-noto-sans-cjk-ttc-fonts"]);
|
|
747
|
+
attempts.push(["yum", "install", "-y", "fontconfig", "google-noto-cjk-fonts"]);
|
|
748
|
+
break;
|
|
749
|
+
case "arch":
|
|
750
|
+
attempts.push(["pacman", "-Sy", "--noconfirm", "fontconfig", "noto-fonts-cjk"]);
|
|
751
|
+
break;
|
|
752
|
+
case "alpine":
|
|
753
|
+
attempts.push(["apk", "add", "fontconfig", "font-noto-cjk"]);
|
|
754
|
+
break;
|
|
755
|
+
default:
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
let attemptedInstall = false;
|
|
759
|
+
for (const baseArgv of attempts) {
|
|
760
|
+
if (!isCommandAvailable(baseArgv[0])) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
const argv = buildPrivilegedCommand(baseArgv, nonInteractive);
|
|
764
|
+
if (!argv) {
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
attemptedInstall = true;
|
|
768
|
+
if (!runSetupCommand(argv, `Running ${argv.join(" ")}`)) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
if (isCommandAvailable("fc-cache")) {
|
|
772
|
+
runSetupCommand(["fc-cache", "-fv"], "Refreshing font cache with fc-cache -fv");
|
|
773
|
+
}
|
|
774
|
+
if (isCommandAvailable("fc-list") && hasChineseFonts()) {
|
|
775
|
+
console.log("Chinese fonts installed successfully.");
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
console.warn("Warning: automatic Chinese font setup did not complete.");
|
|
780
|
+
if (!attemptedInstall && nonInteractive) {
|
|
781
|
+
console.warn("Non-interactive mode skipped any sudo password prompt.");
|
|
782
|
+
}
|
|
783
|
+
console.warn("Manual install examples:");
|
|
784
|
+
for (const command of getManualFontCommands(distro)) {
|
|
785
|
+
console.warn(` ${command}`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
626
788
|
async function configureOpenClaw(options) {
|
|
627
789
|
const configPath = resolveOpenClawConfigPath(options.configPath);
|
|
628
790
|
const stateDir = resolveStateDir(configPath);
|
|
@@ -636,6 +798,9 @@ async function configureOpenClaw(options) {
|
|
|
636
798
|
if (options.pythonSetup) {
|
|
637
799
|
await setupPythonDeps(config.pythonWorkdir, options.nonInteractive);
|
|
638
800
|
}
|
|
801
|
+
if (options.fontSetup) {
|
|
802
|
+
await setupLinuxChineseFonts(options.nonInteractive);
|
|
803
|
+
}
|
|
639
804
|
applyPluginConfig(root, config, target);
|
|
640
805
|
const backupPath = await writeConfig(configPath, root);
|
|
641
806
|
console.log("");
|
package/dist/plugin-commands.js
CHANGED
|
@@ -208,7 +208,7 @@ export function registerPluginCommands(api, tools, app) {
|
|
|
208
208
|
},
|
|
209
209
|
{
|
|
210
210
|
name: "ta_testalert",
|
|
211
|
-
description: "
|
|
211
|
+
description: "发送一条文本 + PNG 测试告警,不经过 AI 对话。",
|
|
212
212
|
requireAuth: true,
|
|
213
213
|
handler: async () => ({
|
|
214
214
|
text: await runToolText(testAlert),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AlertService } from "../services/alert-service.js";
|
|
2
|
-
|
|
2
|
+
import { AlertMediaService } from "../services/alert-media-service.js";
|
|
3
|
+
export declare function testAlertTool(alertService: AlertService, alertMediaService: AlertMediaService): {
|
|
3
4
|
name: string;
|
|
4
5
|
description: string;
|
|
5
6
|
optional: boolean;
|
|
@@ -1,21 +1,101 @@
|
|
|
1
1
|
import { formatChinaDateTime } from "../utils/china-time.js";
|
|
2
|
-
export function testAlertTool(alertService) {
|
|
2
|
+
export function testAlertTool(alertService, alertMediaService) {
|
|
3
3
|
return {
|
|
4
4
|
name: "test_alert",
|
|
5
|
-
description: "Send a test alert through the configured OpenClaw alert delivery path.",
|
|
5
|
+
description: "Send a text plus PNG test alert through the configured OpenClaw alert delivery path.",
|
|
6
6
|
optional: true,
|
|
7
7
|
async run() {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
const now = formatChinaDateTime();
|
|
9
|
+
const message = alertService.formatSystemNotification("🧪 TickFlow 测试告警", [
|
|
10
|
+
`时间: ${now}`,
|
|
11
|
+
"说明: 这是一条手动触发的测试消息,用于验证文本与 PNG 告警卡投递链路正常。",
|
|
12
|
+
]);
|
|
13
|
+
let mediaFile = null;
|
|
14
|
+
try {
|
|
15
|
+
mediaFile = await alertMediaService.writeAlertCard({
|
|
16
|
+
symbol: "000001.SZ",
|
|
17
|
+
ruleName: "test_alert",
|
|
18
|
+
image: buildTestAlertImage(now),
|
|
19
|
+
});
|
|
14
20
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
catch (error) {
|
|
22
|
+
const textOnlyResult = await alertService.sendWithResult({ message });
|
|
23
|
+
if (textOnlyResult.ok) {
|
|
24
|
+
return [
|
|
25
|
+
"⚠️ 测试告警文本已发送,但 PNG 生成失败",
|
|
26
|
+
`原因: ${formatErrorMessage(error)}`,
|
|
27
|
+
].join("\n");
|
|
28
|
+
}
|
|
29
|
+
const detail = textOnlyResult.error ?? alertService.getLastError() ?? "未知错误";
|
|
30
|
+
return [
|
|
31
|
+
"❌ 测试告警发送失败",
|
|
32
|
+
`PNG 生成失败: ${formatErrorMessage(error)}`,
|
|
33
|
+
`文本发送失败: ${detail}`,
|
|
34
|
+
].join("\n");
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const result = await alertService.sendWithResult({
|
|
38
|
+
message,
|
|
39
|
+
mediaPath: mediaFile.filePath,
|
|
40
|
+
mediaLocalRoots: mediaFile.mediaLocalRoots,
|
|
41
|
+
filename: mediaFile.filename,
|
|
42
|
+
});
|
|
43
|
+
if (result.ok && result.mediaDelivered) {
|
|
44
|
+
return "✅ 测试告警发送成功(文本 + PNG)";
|
|
45
|
+
}
|
|
46
|
+
if (result.ok) {
|
|
47
|
+
return "⚠️ 测试告警文本已发送,但 PNG 未送达,已回退为纯文本";
|
|
48
|
+
}
|
|
49
|
+
const detail = result.error ?? alertService.getLastError();
|
|
50
|
+
return detail
|
|
51
|
+
? `❌ 测试告警发送失败\n原因: ${detail}`
|
|
52
|
+
: "❌ 测试告警发送失败";
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
if (mediaFile) {
|
|
56
|
+
await alertMediaService.removeFile(mediaFile.filePath).catch(() => { });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function buildTestAlertImage(now) {
|
|
63
|
+
const triggerPrice = 12.18;
|
|
64
|
+
const currentPrice = 12.36;
|
|
65
|
+
return {
|
|
66
|
+
tone: "breakthrough",
|
|
67
|
+
alertLabel: "测试告警",
|
|
68
|
+
name: "平安银行",
|
|
69
|
+
symbol: "000001.SZ",
|
|
70
|
+
timestampLabel: `测试告警 | ${now.slice(0, 16)}`,
|
|
71
|
+
currentPrice,
|
|
72
|
+
triggerPrice,
|
|
73
|
+
changePct: 2.15,
|
|
74
|
+
distancePct: ((currentPrice - triggerPrice) / triggerPrice) * 100,
|
|
75
|
+
costPrice: 11.92,
|
|
76
|
+
profitPct: ((currentPrice - 11.92) / 11.92) * 100,
|
|
77
|
+
note: "用于验证 PNG 告警卡发送、媒体上传与纯文本回退链路。",
|
|
78
|
+
points: [
|
|
79
|
+
{ time: "09:30", price: 12.02 },
|
|
80
|
+
{ time: "10:00", price: 12.08 },
|
|
81
|
+
{ time: "10:30", price: 12.12 },
|
|
82
|
+
{ time: "11:30", price: 12.15 },
|
|
83
|
+
{ time: "13:00", price: 12.19 },
|
|
84
|
+
{ time: "14:00", price: 12.27 },
|
|
85
|
+
{ time: "15:00", price: currentPrice },
|
|
86
|
+
],
|
|
87
|
+
levels: {
|
|
88
|
+
stopLoss: 11.86,
|
|
89
|
+
support: 12.08,
|
|
90
|
+
resistance: 12.30,
|
|
91
|
+
breakthrough: triggerPrice,
|
|
92
|
+
takeProfit: 12.68,
|
|
19
93
|
},
|
|
20
94
|
};
|
|
21
95
|
}
|
|
96
|
+
function formatErrorMessage(error) {
|
|
97
|
+
if (error instanceof Error) {
|
|
98
|
+
return error.message;
|
|
99
|
+
}
|
|
100
|
+
return String(error);
|
|
101
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "tickflow-assist",
|
|
3
3
|
"name": "TickFlow Assist",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.13",
|
|
5
5
|
"description": "A-share watchlist analysis, monitoring, and alert delivery powered by TickFlow and OpenClaw.",
|
|
6
6
|
"skills": [
|
|
7
7
|
"skills"
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"configSchema": {
|
|
10
10
|
"type": "object",
|
|
11
11
|
"additionalProperties": false,
|
|
12
|
-
"required": ["tickflowApiKey", "llmApiKey"],
|
|
13
12
|
"properties": {
|
|
14
13
|
"tickflowApiUrl": {
|
|
15
14
|
"type": "string",
|
|
@@ -19,7 +18,7 @@
|
|
|
19
18
|
"tickflowApiKey": {
|
|
20
19
|
"type": "string",
|
|
21
20
|
"minLength": 1,
|
|
22
|
-
"description": "
|
|
21
|
+
"description": "TickFlow API key used for market and financial data. Required before using the plugin."
|
|
23
22
|
},
|
|
24
23
|
"tickflowApiKeyLevel": {
|
|
25
24
|
"type": "string",
|
|
@@ -45,7 +44,7 @@
|
|
|
45
44
|
"llmApiKey": {
|
|
46
45
|
"type": "string",
|
|
47
46
|
"minLength": 1,
|
|
48
|
-
"description": "
|
|
47
|
+
"description": "API key for the analysis model endpoint configured in llmBaseUrl. Required before using analysis features."
|
|
49
48
|
},
|
|
50
49
|
"llmModel": {
|
|
51
50
|
"type": "string",
|
|
@@ -114,7 +113,7 @@
|
|
|
114
113
|
"uiHints": {
|
|
115
114
|
"tickflowApiKey": {
|
|
116
115
|
"label": "TickFlow API Key",
|
|
117
|
-
"help": "Required
|
|
116
|
+
"help": "Required before using TickFlow market and financial data.",
|
|
118
117
|
"sensitive": true
|
|
119
118
|
},
|
|
120
119
|
"tickflowApiKeyLevel": {
|
|
@@ -132,7 +131,7 @@
|
|
|
132
131
|
},
|
|
133
132
|
"llmApiKey": {
|
|
134
133
|
"label": "LLM API Key",
|
|
135
|
-
"help": "Required
|
|
134
|
+
"help": "Required before using analysis model requests.",
|
|
136
135
|
"sensitive": true
|
|
137
136
|
},
|
|
138
137
|
"llmModel": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tickflow-assist",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "OpenClaw plugin for TickFlow-based A-share analysis, monitoring, and alerting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dev": "tsc -p tsconfig.json --watch",
|
|
30
30
|
"prepack": "npm run build && node ./scripts/prepare-package-assets.mjs",
|
|
31
31
|
"postpack": "node ./scripts/restore-package-assets.mjs",
|
|
32
|
-
"test": "npm run build && node --test dist/plugin-registration.test.js",
|
|
32
|
+
"test": "npm run build && node --test dist/plugin-registration.test.js dist/tools/test-alert.tool.test.js",
|
|
33
33
|
"community-setup": "node dist/dev/tickflow-assist-cli.js configure-openclaw",
|
|
34
34
|
"tool": "node dist/dev/run-tool.js",
|
|
35
35
|
"monitor-loop": "node dist/dev/run-monitor-loop.js",
|