tickflow-assist 0.2.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/README.md +151 -0
- package/day_future.txt +8797 -0
- package/dist/analysis/orchestrators/composite-analysis.orchestrator.d.ts +35 -0
- package/dist/analysis/orchestrators/composite-analysis.orchestrator.js +232 -0
- package/dist/analysis/parsers/json-block.parser.d.ts +1 -0
- package/dist/analysis/parsers/json-block.parser.js +13 -0
- package/dist/analysis/parsers/key-levels.parser.d.ts +5 -0
- package/dist/analysis/parsers/key-levels.parser.js +61 -0
- package/dist/analysis/parsers/post-close-review.parser.d.ts +2 -0
- package/dist/analysis/parsers/post-close-review.parser.js +79 -0
- package/dist/analysis/parsers/watchlist-profile.parser.d.ts +6 -0
- package/dist/analysis/parsers/watchlist-profile.parser.js +93 -0
- package/dist/analysis/providers/financial-analysis.provider.d.ts +12 -0
- package/dist/analysis/providers/financial-analysis.provider.js +85 -0
- package/dist/analysis/providers/market-analysis.provider.d.ts +27 -0
- package/dist/analysis/providers/market-analysis.provider.js +187 -0
- package/dist/analysis/providers/news-analysis.provider.d.ts +9 -0
- package/dist/analysis/providers/news-analysis.provider.js +60 -0
- package/dist/analysis/tasks/analysis-step-task.d.ts +11 -0
- package/dist/analysis/tasks/analysis-step-task.js +1 -0
- package/dist/analysis/tasks/analysis-task.d.ts +13 -0
- package/dist/analysis/tasks/analysis-task.js +1 -0
- package/dist/analysis/tasks/composite-stock-analysis.task.d.ts +17 -0
- package/dist/analysis/tasks/composite-stock-analysis.task.js +47 -0
- package/dist/analysis/tasks/financial-fundamental-lite.task.d.ts +10 -0
- package/dist/analysis/tasks/financial-fundamental-lite.task.js +21 -0
- package/dist/analysis/tasks/financial-fundamental.task.d.ts +12 -0
- package/dist/analysis/tasks/financial-fundamental.task.js +64 -0
- package/dist/analysis/tasks/kline-technical-signal.task.d.ts +10 -0
- package/dist/analysis/tasks/kline-technical-signal.task.js +41 -0
- package/dist/analysis/tasks/kline-technical.task.d.ts +32 -0
- package/dist/analysis/tasks/kline-technical.task.js +61 -0
- package/dist/analysis/tasks/news-catalyst.task.d.ts +11 -0
- package/dist/analysis/tasks/news-catalyst.task.js +62 -0
- package/dist/analysis/tasks/post-close-review.task.d.ts +12 -0
- package/dist/analysis/tasks/post-close-review.task.js +35 -0
- package/dist/analysis/types/composite-analysis.d.ts +123 -0
- package/dist/analysis/types/composite-analysis.js +1 -0
- package/dist/background/daily-update.worker.d.ts +50 -0
- package/dist/background/daily-update.worker.js +546 -0
- package/dist/background/realtime-monitor.worker.d.ts +8 -0
- package/dist/background/realtime-monitor.worker.js +28 -0
- package/dist/bootstrap.d.ts +45 -0
- package/dist/bootstrap.js +214 -0
- package/dist/config/normalize.d.ts +4 -0
- package/dist/config/normalize.js +99 -0
- package/dist/config/schema.d.ts +23 -0
- package/dist/config/schema.js +18 -0
- package/dist/config/tickflow-access.d.ts +4 -0
- package/dist/config/tickflow-access.js +28 -0
- package/dist/constants/market-indexes.d.ts +5 -0
- package/dist/constants/market-indexes.js +4 -0
- package/dist/dev/run-daily-update-loop.d.ts +1 -0
- package/dist/dev/run-daily-update-loop.js +48 -0
- package/dist/dev/run-monitor-loop.d.ts +1 -0
- package/dist/dev/run-monitor-loop.js +60 -0
- package/dist/dev/run-tool.d.ts +1 -0
- package/dist/dev/run-tool.js +49 -0
- package/dist/dev/tickflow-assist-cli.d.ts +2 -0
- package/dist/dev/tickflow-assist-cli.js +525 -0
- package/dist/dev/validate-mx-search.d.ts +1 -0
- package/dist/dev/validate-mx-search.js +212 -0
- package/dist/plugin-commands.d.ts +3 -0
- package/dist/plugin-commands.js +229 -0
- package/dist/plugin.d.ts +8 -0
- package/dist/plugin.js +151 -0
- package/dist/prompts/analysis/common-system-prompt.d.ts +1 -0
- package/dist/prompts/analysis/common-system-prompt.js +44 -0
- package/dist/prompts/analysis/composite-analysis-user-prompt.d.ts +11 -0
- package/dist/prompts/analysis/composite-analysis-user-prompt.js +102 -0
- package/dist/prompts/analysis/financial-analysis-user-prompt.d.ts +7 -0
- package/dist/prompts/analysis/financial-analysis-user-prompt.js +106 -0
- package/dist/prompts/analysis/financial-lite-analysis-user-prompt.d.ts +7 -0
- package/dist/prompts/analysis/financial-lite-analysis-user-prompt.js +59 -0
- package/dist/prompts/analysis/index.d.ts +8 -0
- package/dist/prompts/analysis/index.js +8 -0
- package/dist/prompts/analysis/kline-analysis-user-prompt.d.ts +13 -0
- package/dist/prompts/analysis/kline-analysis-user-prompt.js +215 -0
- package/dist/prompts/analysis/news-analysis-user-prompt.d.ts +8 -0
- package/dist/prompts/analysis/news-analysis-user-prompt.js +57 -0
- package/dist/prompts/analysis/post-close-review-user-prompt.d.ts +3 -0
- package/dist/prompts/analysis/post-close-review-user-prompt.js +129 -0
- package/dist/prompts/analysis/watchlist-profile-extraction-prompt.d.ts +7 -0
- package/dist/prompts/analysis/watchlist-profile-extraction-prompt.js +52 -0
- package/dist/prompts/analysis-system-prompt.d.ts +1 -0
- package/dist/prompts/analysis-system-prompt.js +1 -0
- package/dist/prompts/analysis-user-prompt.d.ts +1 -0
- package/dist/prompts/analysis-user-prompt.js +1 -0
- package/dist/runtime/daily-update-process.d.ts +3 -0
- package/dist/runtime/daily-update-process.js +24 -0
- package/dist/runtime/monitor-process.d.ts +3 -0
- package/dist/runtime/monitor-process.js +24 -0
- package/dist/runtime/plugin-api.d.ts +22 -0
- package/dist/runtime/plugin-api.js +2 -0
- package/dist/runtime/process-config.d.ts +8 -0
- package/dist/runtime/process-config.js +35 -0
- package/dist/services/alert-service.d.ts +45 -0
- package/dist/services/alert-service.js +198 -0
- package/dist/services/analysis-service.d.ts +20 -0
- package/dist/services/analysis-service.js +73 -0
- package/dist/services/analysis-view-service.d.ts +23 -0
- package/dist/services/analysis-view-service.js +343 -0
- package/dist/services/financial-lite-service.d.ts +23 -0
- package/dist/services/financial-lite-service.js +201 -0
- package/dist/services/financial-service.d.ts +20 -0
- package/dist/services/financial-service.js +47 -0
- package/dist/services/indicator-service.d.ts +9 -0
- package/dist/services/indicator-service.js +67 -0
- package/dist/services/instrument-service.d.ts +6 -0
- package/dist/services/instrument-service.js +21 -0
- package/dist/services/key-level-service.d.ts +2 -0
- package/dist/services/key-level-service.js +2 -0
- package/dist/services/key-levels-backtest-service.d.ts +71 -0
- package/dist/services/key-levels-backtest-service.js +427 -0
- package/dist/services/kline-service.d.ts +19 -0
- package/dist/services/kline-service.js +91 -0
- package/dist/services/monitor-service.d.ts +44 -0
- package/dist/services/monitor-service.js +598 -0
- package/dist/services/mx-search-service.d.ts +22 -0
- package/dist/services/mx-search-service.js +286 -0
- package/dist/services/post-close-review-service.d.ts +31 -0
- package/dist/services/post-close-review-service.js +402 -0
- package/dist/services/quote-service.d.ts +7 -0
- package/dist/services/quote-service.js +9 -0
- package/dist/services/review-memory-service.d.ts +7 -0
- package/dist/services/review-memory-service.js +76 -0
- package/dist/services/tickflow-client.d.ts +43 -0
- package/dist/services/tickflow-client.js +126 -0
- package/dist/services/trading-calendar-service.d.ts +20 -0
- package/dist/services/trading-calendar-service.js +102 -0
- package/dist/services/update-service.d.ts +21 -0
- package/dist/services/update-service.js +130 -0
- package/dist/services/watchlist-profile-service.d.ts +20 -0
- package/dist/services/watchlist-profile-service.js +76 -0
- package/dist/services/watchlist-service.d.ts +43 -0
- package/dist/services/watchlist-service.js +204 -0
- package/dist/storage/db.d.ts +23 -0
- package/dist/storage/db.js +70 -0
- package/dist/storage/repositories/alert-log-repo.d.ts +15 -0
- package/dist/storage/repositories/alert-log-repo.js +54 -0
- package/dist/storage/repositories/analysis-log-repo.d.ts +8 -0
- package/dist/storage/repositories/analysis-log-repo.js +48 -0
- package/dist/storage/repositories/composite-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/composite-analysis-repo.js +116 -0
- package/dist/storage/repositories/financial-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/financial-analysis-repo.js +107 -0
- package/dist/storage/repositories/indicators-repo.d.ts +8 -0
- package/dist/storage/repositories/indicators-repo.js +98 -0
- package/dist/storage/repositories/intraday-klines-repo.d.ts +9 -0
- package/dist/storage/repositories/intraday-klines-repo.js +102 -0
- package/dist/storage/repositories/key-levels-history-repo.d.ts +9 -0
- package/dist/storage/repositories/key-levels-history-repo.js +91 -0
- package/dist/storage/repositories/key-levels-repo.d.ts +9 -0
- package/dist/storage/repositories/key-levels-repo.js +83 -0
- package/dist/storage/repositories/klines-repo.d.ts +8 -0
- package/dist/storage/repositories/klines-repo.js +60 -0
- package/dist/storage/repositories/news-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/news-analysis-repo.js +107 -0
- package/dist/storage/repositories/technical-analysis-repo.d.ts +9 -0
- package/dist/storage/repositories/technical-analysis-repo.js +80 -0
- package/dist/storage/repositories/watchlist-repo.d.ts +10 -0
- package/dist/storage/repositories/watchlist-repo.js +124 -0
- package/dist/storage/schemas.d.ts +13 -0
- package/dist/storage/schemas.js +177 -0
- package/dist/tools/add-stock.tool.d.ts +13 -0
- package/dist/tools/add-stock.tool.js +123 -0
- package/dist/tools/analyze.tool.d.ts +8 -0
- package/dist/tools/analyze.tool.js +24 -0
- package/dist/tools/backtest-key-levels.tool.d.ts +8 -0
- package/dist/tools/backtest-key-levels.tool.js +43 -0
- package/dist/tools/daily-update-status.tool.d.ts +6 -0
- package/dist/tools/daily-update-status.tool.js +9 -0
- package/dist/tools/fetch-financials.tool.d.ts +8 -0
- package/dist/tools/fetch-financials.tool.js +224 -0
- package/dist/tools/fetch-intraday-klines.tool.d.ts +11 -0
- package/dist/tools/fetch-intraday-klines.tool.js +58 -0
- package/dist/tools/fetch-klines.tool.d.ts +11 -0
- package/dist/tools/fetch-klines.tool.js +61 -0
- package/dist/tools/list-watchlist.tool.d.ts +6 -0
- package/dist/tools/list-watchlist.tool.js +22 -0
- package/dist/tools/monitor-status.tool.d.ts +6 -0
- package/dist/tools/monitor-status.tool.js +9 -0
- package/dist/tools/mx-search.tool.d.ts +8 -0
- package/dist/tools/mx-search.tool.js +77 -0
- package/dist/tools/mx-select-stock.tool.d.ts +8 -0
- package/dist/tools/mx-select-stock.tool.js +118 -0
- package/dist/tools/query-database.tool.d.ts +8 -0
- package/dist/tools/query-database.tool.js +283 -0
- package/dist/tools/refresh-watchlist-names.tool.d.ts +7 -0
- package/dist/tools/refresh-watchlist-names.tool.js +17 -0
- package/dist/tools/refresh-watchlist-profiles.tool.d.ts +9 -0
- package/dist/tools/refresh-watchlist-profiles.tool.js +67 -0
- package/dist/tools/remove-stock.tool.d.ts +9 -0
- package/dist/tools/remove-stock.tool.js +27 -0
- package/dist/tools/start-daily-update.tool.d.ts +10 -0
- package/dist/tools/start-daily-update.tool.js +23 -0
- package/dist/tools/start-monitor.tool.d.ts +9 -0
- package/dist/tools/start-monitor.tool.js +52 -0
- package/dist/tools/stop-daily-update.tool.d.ts +9 -0
- package/dist/tools/stop-daily-update.tool.js +20 -0
- package/dist/tools/stop-monitor.tool.d.ts +9 -0
- package/dist/tools/stop-monitor.tool.js +44 -0
- package/dist/tools/test-alert.tool.d.ts +7 -0
- package/dist/tools/test-alert.tool.js +21 -0
- package/dist/tools/update-all.tool.d.ts +9 -0
- package/dist/tools/update-all.tool.js +17 -0
- package/dist/tools/view-analysis.tool.d.ts +8 -0
- package/dist/tools/view-analysis.tool.js +95 -0
- package/dist/types/daily-update.d.ts +26 -0
- package/dist/types/daily-update.js +1 -0
- package/dist/types/domain.d.ts +140 -0
- package/dist/types/domain.js +1 -0
- package/dist/types/indicator.d.ts +43 -0
- package/dist/types/indicator.js +1 -0
- package/dist/types/monitor.d.ts +17 -0
- package/dist/types/monitor.js +1 -0
- package/dist/types/mx-search.d.ts +14 -0
- package/dist/types/mx-search.js +1 -0
- package/dist/types/mx-select-stock.d.ts +28 -0
- package/dist/types/mx-select-stock.js +1 -0
- package/dist/types/tickflow.d.ts +133 -0
- package/dist/types/tickflow.js +1 -0
- package/dist/utils/abortable-sleep.d.ts +1 -0
- package/dist/utils/abortable-sleep.js +20 -0
- package/dist/utils/china-time.d.ts +3 -0
- package/dist/utils/china-time.js +18 -0
- package/dist/utils/cost-price.d.ts +3 -0
- package/dist/utils/cost-price.js +18 -0
- package/dist/utils/format.d.ts +1 -0
- package/dist/utils/format.js +3 -0
- package/dist/utils/process.d.ts +5 -0
- package/dist/utils/process.js +1 -0
- package/dist/utils/symbol.d.ts +1 -0
- package/dist/utils/symbol.js +13 -0
- package/docs/installation.md +391 -0
- package/docs/usage.md +244 -0
- package/openclaw.plugin.json +116 -0
- package/package.json +57 -0
- package/python/indicator_runner.py +31 -0
- package/python/indicators.py +148 -0
- package/python/pyproject.toml +9 -0
- package/python/requirements.txt +3 -0
- package/python/uv.lock +366 -0
- package/skills/database-query/SKILL.md +58 -0
- package/skills/stock-analysis/SKILL.md +106 -0
- package/skills/usage-help/SKILL.md +102 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { createInterface } from "node:readline/promises";
|
|
8
|
+
import JSON5 from "json5";
|
|
9
|
+
const PLUGIN_ID = "tickflow-assist";
|
|
10
|
+
const DEFAULT_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
tickflowApiUrl: "https://api.tickflow.org",
|
|
13
|
+
tickflowApiKeyLevel: "Free",
|
|
14
|
+
mxSearchApiUrl: "https://mkapi2.dfcfs.com/finskillshub/api/claw",
|
|
15
|
+
mxSearchApiKey: "",
|
|
16
|
+
llmBaseUrl: "https://api.openai.com/v1",
|
|
17
|
+
llmModel: "gpt-4o",
|
|
18
|
+
requestInterval: 30,
|
|
19
|
+
dailyUpdateNotify: true,
|
|
20
|
+
alertChannel: "telegram",
|
|
21
|
+
openclawCliBin: "openclaw",
|
|
22
|
+
alertAccount: "",
|
|
23
|
+
pythonBin: "uv",
|
|
24
|
+
pythonArgs: ["run", "python"],
|
|
25
|
+
};
|
|
26
|
+
function printUsage() {
|
|
27
|
+
console.log(`TickFlow Assist CLI
|
|
28
|
+
|
|
29
|
+
Usage:
|
|
30
|
+
tickflow-assist configure-openclaw [options]
|
|
31
|
+
|
|
32
|
+
Options:
|
|
33
|
+
--config-path <path> Override OpenClaw config path
|
|
34
|
+
--plugin-dir <path> Override installed plugin directory
|
|
35
|
+
--agent <id> Apply tools.allow to a specific agent
|
|
36
|
+
--global Apply tools.allow to top-level tools config
|
|
37
|
+
--non-interactive Use existing config / flags only, no prompts
|
|
38
|
+
--no-enable Do not run 'openclaw plugins enable'
|
|
39
|
+
--no-restart Do not run 'openclaw gateway restart'
|
|
40
|
+
--openclaw-bin <path> OpenClaw CLI binary, default: openclaw
|
|
41
|
+
--tickflow-api-key <key>
|
|
42
|
+
--tickflow-api-key-level <Free|Start|Pro|Expert>
|
|
43
|
+
--mx-search-api-key <key>
|
|
44
|
+
--llm-base-url <url>
|
|
45
|
+
--llm-api-key <key>
|
|
46
|
+
--llm-model <name>
|
|
47
|
+
--alert-channel <name>
|
|
48
|
+
--alert-account <name>
|
|
49
|
+
--alert-target <target>
|
|
50
|
+
-h, --help Show this help
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
function parseArgs(argv) {
|
|
54
|
+
const args = [...argv];
|
|
55
|
+
let command = "help";
|
|
56
|
+
const options = {
|
|
57
|
+
command,
|
|
58
|
+
globalTarget: false,
|
|
59
|
+
nonInteractive: false,
|
|
60
|
+
restart: true,
|
|
61
|
+
enable: true,
|
|
62
|
+
openclawBin: DEFAULTS.openclawCliBin,
|
|
63
|
+
overrides: {},
|
|
64
|
+
};
|
|
65
|
+
const first = args[0];
|
|
66
|
+
if (first && !first.startsWith("-")) {
|
|
67
|
+
if (first === "configure-openclaw") {
|
|
68
|
+
command = "configure-openclaw";
|
|
69
|
+
args.shift();
|
|
70
|
+
}
|
|
71
|
+
else if (first === "help") {
|
|
72
|
+
command = "help";
|
|
73
|
+
args.shift();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
command = "configure-openclaw";
|
|
78
|
+
}
|
|
79
|
+
options.command = command;
|
|
80
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
81
|
+
const token = args[index];
|
|
82
|
+
const next = args[index + 1];
|
|
83
|
+
const requireValue = (flag) => {
|
|
84
|
+
if (!next || next.startsWith("-")) {
|
|
85
|
+
throw new Error(`${flag} requires a value`);
|
|
86
|
+
}
|
|
87
|
+
index += 1;
|
|
88
|
+
return next;
|
|
89
|
+
};
|
|
90
|
+
switch (token) {
|
|
91
|
+
case "-h":
|
|
92
|
+
case "--help":
|
|
93
|
+
options.command = "help";
|
|
94
|
+
break;
|
|
95
|
+
case "--config-path":
|
|
96
|
+
options.configPath = requireValue(token);
|
|
97
|
+
break;
|
|
98
|
+
case "--plugin-dir":
|
|
99
|
+
options.pluginDir = requireValue(token);
|
|
100
|
+
break;
|
|
101
|
+
case "--agent":
|
|
102
|
+
options.agentId = requireValue(token);
|
|
103
|
+
break;
|
|
104
|
+
case "--global":
|
|
105
|
+
options.globalTarget = true;
|
|
106
|
+
break;
|
|
107
|
+
case "--non-interactive":
|
|
108
|
+
options.nonInteractive = true;
|
|
109
|
+
break;
|
|
110
|
+
case "--no-restart":
|
|
111
|
+
options.restart = false;
|
|
112
|
+
break;
|
|
113
|
+
case "--no-enable":
|
|
114
|
+
options.enable = false;
|
|
115
|
+
break;
|
|
116
|
+
case "--openclaw-bin":
|
|
117
|
+
options.openclawBin = requireValue(token);
|
|
118
|
+
break;
|
|
119
|
+
case "--tickflow-api-key":
|
|
120
|
+
options.overrides.tickflowApiKey = requireValue(token);
|
|
121
|
+
break;
|
|
122
|
+
case "--tickflow-api-key-level":
|
|
123
|
+
options.overrides.tickflowApiKeyLevel = normalizeApiKeyLevel(requireValue(token));
|
|
124
|
+
break;
|
|
125
|
+
case "--mx-search-api-key":
|
|
126
|
+
options.overrides.mxSearchApiKey = requireValue(token);
|
|
127
|
+
break;
|
|
128
|
+
case "--llm-base-url":
|
|
129
|
+
options.overrides.llmBaseUrl = requireValue(token);
|
|
130
|
+
break;
|
|
131
|
+
case "--llm-api-key":
|
|
132
|
+
options.overrides.llmApiKey = requireValue(token);
|
|
133
|
+
break;
|
|
134
|
+
case "--llm-model":
|
|
135
|
+
options.overrides.llmModel = requireValue(token);
|
|
136
|
+
break;
|
|
137
|
+
case "--alert-channel":
|
|
138
|
+
options.overrides.alertChannel = requireValue(token);
|
|
139
|
+
break;
|
|
140
|
+
case "--alert-account":
|
|
141
|
+
options.overrides.alertAccount = requireValue(token);
|
|
142
|
+
break;
|
|
143
|
+
case "--alert-target":
|
|
144
|
+
options.overrides.alertTarget = requireValue(token);
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`unknown argument: ${token}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return options;
|
|
151
|
+
}
|
|
152
|
+
function normalizeApiKeyLevel(value) {
|
|
153
|
+
const normalized = value.trim().toLowerCase();
|
|
154
|
+
switch (normalized) {
|
|
155
|
+
case "free":
|
|
156
|
+
return "Free";
|
|
157
|
+
case "start":
|
|
158
|
+
return "Start";
|
|
159
|
+
case "pro":
|
|
160
|
+
return "Pro";
|
|
161
|
+
case "expert":
|
|
162
|
+
return "Expert";
|
|
163
|
+
default:
|
|
164
|
+
throw new Error(`invalid tickflowApiKeyLevel: ${value}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function resolveOpenClawConfigPath(input) {
|
|
168
|
+
return input || process.env.OPENCLAW_CONFIG_PATH || DEFAULT_CONFIG_PATH;
|
|
169
|
+
}
|
|
170
|
+
function resolveStateDir(configPath) {
|
|
171
|
+
return process.env.OPENCLAW_STATE_DIR || process.env.OPENCLAW_HOME || path.dirname(configPath);
|
|
172
|
+
}
|
|
173
|
+
async function readConfigFile(configPath) {
|
|
174
|
+
try {
|
|
175
|
+
const raw = await readFile(configPath, "utf-8");
|
|
176
|
+
const parsed = JSON5.parse(raw);
|
|
177
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
178
|
+
return parsed;
|
|
179
|
+
}
|
|
180
|
+
throw new Error("OpenClaw config must be an object");
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error.code === "ENOENT") {
|
|
184
|
+
return {};
|
|
185
|
+
}
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function getObject(parent, key) {
|
|
190
|
+
const current = parent[key];
|
|
191
|
+
if (typeof current === "object" && current !== null && !Array.isArray(current)) {
|
|
192
|
+
return current;
|
|
193
|
+
}
|
|
194
|
+
const created = {};
|
|
195
|
+
parent[key] = created;
|
|
196
|
+
return created;
|
|
197
|
+
}
|
|
198
|
+
function getArray(parent, key) {
|
|
199
|
+
const current = parent[key];
|
|
200
|
+
if (Array.isArray(current)) {
|
|
201
|
+
return current;
|
|
202
|
+
}
|
|
203
|
+
const created = [];
|
|
204
|
+
parent[key] = created;
|
|
205
|
+
return created;
|
|
206
|
+
}
|
|
207
|
+
function uniqueStrings(values) {
|
|
208
|
+
return [...new Set(values.filter((value) => value.trim()))];
|
|
209
|
+
}
|
|
210
|
+
function mergeAllowlist(target, pluginId) {
|
|
211
|
+
const allow = Array.isArray(target.allow)
|
|
212
|
+
? target.allow.map((value) => String(value))
|
|
213
|
+
: [];
|
|
214
|
+
target.allow = uniqueStrings([...allow, pluginId]);
|
|
215
|
+
}
|
|
216
|
+
function getExistingPluginConfig(root) {
|
|
217
|
+
const plugins = typeof root.plugins === "object" && root.plugins !== null
|
|
218
|
+
? root.plugins
|
|
219
|
+
: {};
|
|
220
|
+
const entries = typeof plugins.entries === "object" && plugins.entries !== null
|
|
221
|
+
? plugins.entries
|
|
222
|
+
: {};
|
|
223
|
+
const entry = typeof entries[PLUGIN_ID] === "object" && entries[PLUGIN_ID] !== null
|
|
224
|
+
? entries[PLUGIN_ID]
|
|
225
|
+
: {};
|
|
226
|
+
const config = typeof entry.config === "object" && entry.config !== null
|
|
227
|
+
? entry.config
|
|
228
|
+
: {};
|
|
229
|
+
const pythonArgs = Array.isArray(config.pythonArgs)
|
|
230
|
+
? config.pythonArgs.map((value) => String(value))
|
|
231
|
+
: undefined;
|
|
232
|
+
const requestInterval = Number(config.requestInterval ?? DEFAULTS.requestInterval);
|
|
233
|
+
const dailyUpdateNotify = typeof config.dailyUpdateNotify === "boolean"
|
|
234
|
+
? config.dailyUpdateNotify
|
|
235
|
+
: DEFAULTS.dailyUpdateNotify;
|
|
236
|
+
return {
|
|
237
|
+
tickflowApiUrl: stringValue(config.tickflowApiUrl, DEFAULTS.tickflowApiUrl),
|
|
238
|
+
tickflowApiKey: stringValue(config.tickflowApiKey),
|
|
239
|
+
tickflowApiKeyLevel: normalizeApiKeyLevel(stringValue(config.tickflowApiKeyLevel, DEFAULTS.tickflowApiKeyLevel)),
|
|
240
|
+
mxSearchApiUrl: stringValue(config.mxSearchApiUrl, DEFAULTS.mxSearchApiUrl),
|
|
241
|
+
mxSearchApiKey: stringValue(config.mxSearchApiKey, DEFAULTS.mxSearchApiKey),
|
|
242
|
+
llmBaseUrl: stringValue(config.llmBaseUrl, DEFAULTS.llmBaseUrl),
|
|
243
|
+
llmApiKey: stringValue(config.llmApiKey),
|
|
244
|
+
llmModel: stringValue(config.llmModel, DEFAULTS.llmModel),
|
|
245
|
+
requestInterval: Number.isFinite(requestInterval) ? Math.max(5, Math.trunc(requestInterval)) : DEFAULTS.requestInterval,
|
|
246
|
+
dailyUpdateNotify,
|
|
247
|
+
alertChannel: stringValue(config.alertChannel, DEFAULTS.alertChannel),
|
|
248
|
+
openclawCliBin: stringValue(config.openclawCliBin, DEFAULTS.openclawCliBin),
|
|
249
|
+
alertAccount: stringValue(config.alertAccount, DEFAULTS.alertAccount),
|
|
250
|
+
alertTarget: stringValue(config.alertTarget),
|
|
251
|
+
pythonBin: stringValue(config.pythonBin, DEFAULTS.pythonBin),
|
|
252
|
+
pythonArgs: pythonArgs && pythonArgs.length > 0 ? pythonArgs : [...DEFAULTS.pythonArgs],
|
|
253
|
+
pythonWorkdir: stringValue(config.pythonWorkdir),
|
|
254
|
+
databasePath: stringValue(config.databasePath),
|
|
255
|
+
calendarFile: stringValue(config.calendarFile),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function stringValue(value, fallback = "") {
|
|
259
|
+
return typeof value === "string" ? value.trim() : fallback;
|
|
260
|
+
}
|
|
261
|
+
function inferAllowTarget(root, options) {
|
|
262
|
+
if (options.globalTarget) {
|
|
263
|
+
return { type: "global" };
|
|
264
|
+
}
|
|
265
|
+
if (options.agentId) {
|
|
266
|
+
return { type: "agent", id: options.agentId };
|
|
267
|
+
}
|
|
268
|
+
const agents = typeof root.agents === "object" && root.agents !== null
|
|
269
|
+
? root.agents
|
|
270
|
+
: null;
|
|
271
|
+
const list = Array.isArray(agents?.list) ? agents.list : [];
|
|
272
|
+
const ids = list
|
|
273
|
+
.filter((entry) => typeof entry === "object" && entry !== null)
|
|
274
|
+
.map((entry) => stringValue(entry.id))
|
|
275
|
+
.filter(Boolean);
|
|
276
|
+
if (ids.includes("stock")) {
|
|
277
|
+
return { type: "agent", id: "stock" };
|
|
278
|
+
}
|
|
279
|
+
if (ids.length === 1) {
|
|
280
|
+
return { type: "agent", id: ids[0] };
|
|
281
|
+
}
|
|
282
|
+
return { type: "global" };
|
|
283
|
+
}
|
|
284
|
+
async function promptForConfig(options, existing, pluginDir) {
|
|
285
|
+
const defaults = {
|
|
286
|
+
tickflowApiUrl: DEFAULTS.tickflowApiUrl,
|
|
287
|
+
tickflowApiKey: "",
|
|
288
|
+
tickflowApiKeyLevel: DEFAULTS.tickflowApiKeyLevel,
|
|
289
|
+
mxSearchApiUrl: DEFAULTS.mxSearchApiUrl,
|
|
290
|
+
mxSearchApiKey: DEFAULTS.mxSearchApiKey,
|
|
291
|
+
llmBaseUrl: DEFAULTS.llmBaseUrl,
|
|
292
|
+
llmApiKey: "",
|
|
293
|
+
llmModel: DEFAULTS.llmModel,
|
|
294
|
+
requestInterval: DEFAULTS.requestInterval,
|
|
295
|
+
dailyUpdateNotify: DEFAULTS.dailyUpdateNotify,
|
|
296
|
+
alertChannel: DEFAULTS.alertChannel,
|
|
297
|
+
openclawCliBin: options.openclawBin || DEFAULTS.openclawCliBin,
|
|
298
|
+
alertAccount: DEFAULTS.alertAccount,
|
|
299
|
+
alertTarget: "",
|
|
300
|
+
pythonBin: DEFAULTS.pythonBin,
|
|
301
|
+
pythonArgs: [...DEFAULTS.pythonArgs],
|
|
302
|
+
pythonWorkdir: path.join(pluginDir, "python"),
|
|
303
|
+
databasePath: path.join(pluginDir, "data", "lancedb"),
|
|
304
|
+
calendarFile: path.join(pluginDir, "day_future.txt"),
|
|
305
|
+
};
|
|
306
|
+
const seed = {
|
|
307
|
+
...defaults,
|
|
308
|
+
...existing,
|
|
309
|
+
...options.overrides,
|
|
310
|
+
openclawCliBin: options.openclawBin || existing.openclawCliBin || DEFAULTS.openclawCliBin,
|
|
311
|
+
pythonWorkdir: path.join(pluginDir, "python"),
|
|
312
|
+
databasePath: path.join(pluginDir, "data", "lancedb"),
|
|
313
|
+
calendarFile: path.join(pluginDir, "day_future.txt"),
|
|
314
|
+
};
|
|
315
|
+
if (options.nonInteractive) {
|
|
316
|
+
assertRequired(seed);
|
|
317
|
+
return seed;
|
|
318
|
+
}
|
|
319
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
320
|
+
try {
|
|
321
|
+
console.log("TickFlow Assist 社区安装配置向导");
|
|
322
|
+
console.log(`OpenClaw 配置文件: ${resolveOpenClawConfigPath(options.configPath)}`);
|
|
323
|
+
console.log(`插件目录: ${pluginDir}`);
|
|
324
|
+
console.log("");
|
|
325
|
+
seed.tickflowApiKey = await promptString(rl, "TickFlow API Key", seed.tickflowApiKey, true);
|
|
326
|
+
seed.tickflowApiKeyLevel = normalizeApiKeyLevel(await promptString(rl, "TickFlow API Key Level [Free/Start/Pro/Expert]", seed.tickflowApiKeyLevel, true));
|
|
327
|
+
seed.mxSearchApiKey = await promptString(rl, "MX Search API Key (可留空)", seed.mxSearchApiKey, false);
|
|
328
|
+
seed.llmBaseUrl = await promptString(rl, "LLM Base URL", seed.llmBaseUrl, true);
|
|
329
|
+
seed.llmApiKey = await promptString(rl, "LLM API Key", seed.llmApiKey, true);
|
|
330
|
+
seed.llmModel = await promptString(rl, "LLM Model", seed.llmModel, true);
|
|
331
|
+
seed.alertChannel = await promptString(rl, "Alert Channel", seed.alertChannel, true);
|
|
332
|
+
seed.alertAccount = await promptString(rl, "Alert Account (可留空)", seed.alertAccount, false);
|
|
333
|
+
seed.alertTarget = await promptString(rl, "Alert Target", seed.alertTarget, true);
|
|
334
|
+
seed.requestInterval = await promptInteger(rl, "Request Interval (seconds)", seed.requestInterval, 5);
|
|
335
|
+
seed.dailyUpdateNotify = await promptBoolean(rl, "Daily Update Notify", seed.dailyUpdateNotify);
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
rl.close();
|
|
339
|
+
}
|
|
340
|
+
assertRequired(seed);
|
|
341
|
+
return seed;
|
|
342
|
+
}
|
|
343
|
+
async function promptString(rl, label, defaultValue, required) {
|
|
344
|
+
while (true) {
|
|
345
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
346
|
+
const answer = (await rl.question(`${label}${suffix}: `)).trim();
|
|
347
|
+
const value = answer || defaultValue;
|
|
348
|
+
if (!required || value) {
|
|
349
|
+
return value;
|
|
350
|
+
}
|
|
351
|
+
console.error(`${label} 不能为空`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async function promptInteger(rl, label, defaultValue, minimum) {
|
|
355
|
+
while (true) {
|
|
356
|
+
const answer = (await rl.question(`${label} [${defaultValue}]: `)).trim();
|
|
357
|
+
const value = answer ? Number(answer) : defaultValue;
|
|
358
|
+
if (Number.isFinite(value) && value >= minimum) {
|
|
359
|
+
return Math.trunc(value);
|
|
360
|
+
}
|
|
361
|
+
console.error(`${label} 必须是 >= ${minimum} 的整数`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function promptBoolean(rl, label, defaultValue) {
|
|
365
|
+
while (true) {
|
|
366
|
+
const answer = (await rl.question(`${label} [${defaultValue ? "Y/n" : "y/N"}]: `)).trim().toLowerCase();
|
|
367
|
+
if (!answer) {
|
|
368
|
+
return defaultValue;
|
|
369
|
+
}
|
|
370
|
+
if (["y", "yes", "1", "true"].includes(answer)) {
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
if (["n", "no", "0", "false"].includes(answer)) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
console.error(`${label} 请输入 y 或 n`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function assertRequired(config) {
|
|
380
|
+
if (!config.tickflowApiKey) {
|
|
381
|
+
throw new Error("tickflowApiKey is required");
|
|
382
|
+
}
|
|
383
|
+
if (!config.llmApiKey) {
|
|
384
|
+
throw new Error("llmApiKey is required");
|
|
385
|
+
}
|
|
386
|
+
if (!config.alertTarget) {
|
|
387
|
+
throw new Error("alertTarget is required");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async function ensurePathNotice(targetPath, label) {
|
|
391
|
+
try {
|
|
392
|
+
await access(targetPath);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
console.warn(`Warning: ${label} not found at ${targetPath}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function applyPluginConfig(root, config, target) {
|
|
399
|
+
const plugins = getObject(root, "plugins");
|
|
400
|
+
plugins.enabled = true;
|
|
401
|
+
const entries = getObject(plugins, "entries");
|
|
402
|
+
const pluginEntry = getObject(entries, PLUGIN_ID);
|
|
403
|
+
pluginEntry.enabled = true;
|
|
404
|
+
pluginEntry.config = {
|
|
405
|
+
tickflowApiUrl: config.tickflowApiUrl,
|
|
406
|
+
tickflowApiKey: config.tickflowApiKey,
|
|
407
|
+
tickflowApiKeyLevel: config.tickflowApiKeyLevel,
|
|
408
|
+
mxSearchApiUrl: config.mxSearchApiUrl,
|
|
409
|
+
mxSearchApiKey: config.mxSearchApiKey,
|
|
410
|
+
llmBaseUrl: config.llmBaseUrl,
|
|
411
|
+
llmApiKey: config.llmApiKey,
|
|
412
|
+
llmModel: config.llmModel,
|
|
413
|
+
databasePath: config.databasePath,
|
|
414
|
+
calendarFile: config.calendarFile,
|
|
415
|
+
requestInterval: config.requestInterval,
|
|
416
|
+
dailyUpdateNotify: config.dailyUpdateNotify,
|
|
417
|
+
alertChannel: config.alertChannel,
|
|
418
|
+
openclawCliBin: config.openclawCliBin,
|
|
419
|
+
alertAccount: config.alertAccount,
|
|
420
|
+
alertTarget: config.alertTarget,
|
|
421
|
+
pythonBin: config.pythonBin,
|
|
422
|
+
pythonArgs: config.pythonArgs,
|
|
423
|
+
pythonWorkdir: config.pythonWorkdir,
|
|
424
|
+
};
|
|
425
|
+
const pluginAllow = Array.isArray(plugins.allow)
|
|
426
|
+
? plugins.allow.map((value) => String(value))
|
|
427
|
+
: [];
|
|
428
|
+
plugins.allow = uniqueStrings([...pluginAllow, PLUGIN_ID]);
|
|
429
|
+
if (target.type === "global") {
|
|
430
|
+
const tools = getObject(root, "tools");
|
|
431
|
+
mergeAllowlist(tools, PLUGIN_ID);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
const agents = getObject(root, "agents");
|
|
435
|
+
const list = getArray(agents, "list");
|
|
436
|
+
let matched = false;
|
|
437
|
+
for (const item of list) {
|
|
438
|
+
if (typeof item !== "object" || item === null) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const entry = item;
|
|
442
|
+
if (stringValue(entry.id) !== target.id) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
const tools = getObject(entry, "tools");
|
|
446
|
+
mergeAllowlist(tools, PLUGIN_ID);
|
|
447
|
+
matched = true;
|
|
448
|
+
}
|
|
449
|
+
if (!matched) {
|
|
450
|
+
list.push({
|
|
451
|
+
id: target.id,
|
|
452
|
+
tools: {
|
|
453
|
+
allow: [PLUGIN_ID],
|
|
454
|
+
},
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
async function writeConfig(configPath, root) {
|
|
459
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
460
|
+
let backupPath = null;
|
|
461
|
+
try {
|
|
462
|
+
const existing = await readFile(configPath, "utf-8");
|
|
463
|
+
backupPath = `${configPath}.backup.${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
464
|
+
await writeFile(backupPath, existing, "utf-8");
|
|
465
|
+
}
|
|
466
|
+
catch (error) {
|
|
467
|
+
if (error.code !== "ENOENT") {
|
|
468
|
+
throw error;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
await writeFile(configPath, `${JSON.stringify(root, null, 2)}\n`, "utf-8");
|
|
472
|
+
return backupPath;
|
|
473
|
+
}
|
|
474
|
+
function runOpenClaw(bin, args, description) {
|
|
475
|
+
const result = spawnSync(bin, args, { stdio: "inherit" });
|
|
476
|
+
if (result.error) {
|
|
477
|
+
console.warn(`Warning: failed to run ${description}: ${result.error.message}`);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (result.status !== 0) {
|
|
481
|
+
console.warn(`Warning: ${description} exited with status ${result.status}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
async function configureOpenClaw(options) {
|
|
485
|
+
const configPath = resolveOpenClawConfigPath(options.configPath);
|
|
486
|
+
const stateDir = resolveStateDir(configPath);
|
|
487
|
+
const pluginDir = path.resolve(options.pluginDir || path.join(stateDir, "extensions", PLUGIN_ID));
|
|
488
|
+
const root = await readConfigFile(configPath);
|
|
489
|
+
const target = inferAllowTarget(root, options);
|
|
490
|
+
const existing = getExistingPluginConfig(root);
|
|
491
|
+
const config = await promptForConfig(options, existing, pluginDir);
|
|
492
|
+
await ensurePathNotice(config.calendarFile, "calendarFile");
|
|
493
|
+
await ensurePathNotice(config.pythonWorkdir, "pythonWorkdir");
|
|
494
|
+
applyPluginConfig(root, config, target);
|
|
495
|
+
const backupPath = await writeConfig(configPath, root);
|
|
496
|
+
console.log("");
|
|
497
|
+
console.log(`Updated OpenClaw config: ${configPath}`);
|
|
498
|
+
if (backupPath) {
|
|
499
|
+
console.log(`Backup created: ${backupPath}`);
|
|
500
|
+
}
|
|
501
|
+
console.log(`Plugin dir: ${pluginDir}`);
|
|
502
|
+
console.log(`Allowlist target: ${target.type === "global" ? "global tools" : `agent:${target.id}`}`);
|
|
503
|
+
if (options.enable) {
|
|
504
|
+
runOpenClaw(options.openclawBin, ["plugins", "enable", PLUGIN_ID], "openclaw plugins enable");
|
|
505
|
+
}
|
|
506
|
+
runOpenClaw(options.openclawBin, ["config", "validate"], "openclaw config validate");
|
|
507
|
+
if (options.restart) {
|
|
508
|
+
runOpenClaw(options.openclawBin, ["gateway", "restart"], "openclaw gateway restart");
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
async function main() {
|
|
512
|
+
try {
|
|
513
|
+
const options = parseArgs(process.argv.slice(2));
|
|
514
|
+
if (options.command === "help") {
|
|
515
|
+
printUsage();
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
await configureOpenClaw(options);
|
|
519
|
+
}
|
|
520
|
+
catch (error) {
|
|
521
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
522
|
+
process.exitCode = 1;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
void main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|