@hedgehog-finance/hedgehog-plugin 1.0.11 → 1.0.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/dist/index.d.ts +10 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/src/channel.d.ts +6 -0
- package/dist/src/channel.js +620 -0
- package/dist/src/channel.js.map +1 -0
- package/dist/src/core/database.d.ts +2 -0
- package/dist/src/core/database.js +220 -0
- package/dist/src/core/database.js.map +1 -0
- package/dist/src/core/logger.d.ts +3 -0
- package/dist/src/core/logger.js +20 -0
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/features/index.d.ts +22 -0
- package/dist/src/features/index.js +8 -0
- package/dist/src/features/index.js.map +1 -0
- package/dist/src/features/watchlist/logic.d.ts +48 -0
- package/dist/src/features/watchlist/logic.js +607 -0
- package/dist/src/features/watchlist/logic.js.map +1 -0
- package/dist/src/features/watchlist/schema.d.ts +85 -0
- package/dist/src/features/watchlist/schema.js +29 -0
- package/dist/src/features/watchlist/schema.js.map +1 -0
- package/dist/src/features/watchlist/store.d.ts +1 -0
- package/dist/src/features/watchlist/store.js +2 -0
- package/dist/src/features/watchlist/store.js.map +1 -0
- package/dist/src/features/watchlist/tools.d.ts +135 -0
- package/dist/src/features/watchlist/tools.js +572 -0
- package/dist/src/features/watchlist/tools.js.map +1 -0
- package/dist/src/runtime.d.ts +5 -0
- package/dist/src/runtime.js +40 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/types.d.ts +99 -0
- package/dist/src/types.js +16 -0
- package/dist/src/types.js.map +1 -0
- package/index.ts +5 -5
- package/openclaw.plugin.json +2 -2
- package/package.json +24 -7
- package/src/channel.ts +35 -13
- package/src/core/database.ts +90 -3
- package/src/features/index.ts +2 -1
- package/src/features/watchlist/logic.ts +503 -128
- package/src/features/watchlist/schema.ts +1 -6
- package/src/features/watchlist/tools.ts +248 -103
- package/src/runtime.ts +3 -3
- package/src/types.ts +1 -1
- package/tsconfig.json +0 -16
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,IAAI,OAAO,GAAyB,IAAI,CAAC;AACzC,IAAI,MAAM,GAAW,EAAE,CAAC;AACxB,IAAI,SAAS,GAAW,EAAE,CAAC;AAE3B,MAAM,UAAU,kBAAkB,CAAC,IAAmB;IACrD,OAAO,GAAG,IAAI,CAAC;IAEf,IAAI,CAAC;QACJ,sCAAsC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,EAAE,CAAyC,CAAC;QACnF,MAAM,aAAa,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,kBAAkB,CAAC,CAAC;QACzE,MAAM,YAAY,GAAG,aAAa,EAAE,SAAS;YAC5C,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,SAAS;YAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,oBAAoB,CAAC,CAAC;QAE5D,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;QACxD,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,6BAA6B,CAAC,CAAC;IACzD,CAAC;AACF,CAAC;AAED,MAAM,UAAU,SAAS;IACxB,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IACtE,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,YAAY;IAC3B,IAAI,CAAC,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC5E,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,kBAAkB;IACjC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACxE,OAAO,OAAO,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Hedgehog Finance Resolved Account
|
|
4
|
+
*/
|
|
5
|
+
export interface HedgehogFinanceResolvedAccount {
|
|
6
|
+
accountId: string;
|
|
7
|
+
config: {
|
|
8
|
+
token: string;
|
|
9
|
+
code: string;
|
|
10
|
+
};
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
configured: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Inbound Message from Relay (Manual Handling)
|
|
16
|
+
*/
|
|
17
|
+
export interface RelayInboundMessage {
|
|
18
|
+
type: "req" | "reply" | "item_event" | "usage" | "model" | "reasoning";
|
|
19
|
+
from: string;
|
|
20
|
+
chatId: string;
|
|
21
|
+
id: string;
|
|
22
|
+
text?: string;
|
|
23
|
+
method?: string;
|
|
24
|
+
params?: any;
|
|
25
|
+
replyTo?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Session entry structure in sessions.json
|
|
29
|
+
*/
|
|
30
|
+
export interface OpenClawSessionEntry {
|
|
31
|
+
sessionId: string;
|
|
32
|
+
inputTokens?: number;
|
|
33
|
+
outputTokens?: number;
|
|
34
|
+
totalTokens?: number;
|
|
35
|
+
cacheRead?: number;
|
|
36
|
+
estimatedCostUsd?: number;
|
|
37
|
+
model?: string;
|
|
38
|
+
modelProvider?: string;
|
|
39
|
+
updatedAt?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Normalized Usage for UI and Internal logic
|
|
43
|
+
*/
|
|
44
|
+
export interface TurnUsage {
|
|
45
|
+
input: number;
|
|
46
|
+
output: number;
|
|
47
|
+
total: number;
|
|
48
|
+
cacheRead: number;
|
|
49
|
+
cost: number;
|
|
50
|
+
model: string;
|
|
51
|
+
provider: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Stock Classification Result (AI Schema)
|
|
55
|
+
*/
|
|
56
|
+
export declare const StockClassificationSchema: z.ZodObject<{
|
|
57
|
+
industry: z.ZodObject<{
|
|
58
|
+
name: z.ZodString;
|
|
59
|
+
weight: z.ZodDefault<z.ZodNumber>;
|
|
60
|
+
}, "strip", z.ZodTypeAny, {
|
|
61
|
+
name: string;
|
|
62
|
+
weight: number;
|
|
63
|
+
}, {
|
|
64
|
+
name: string;
|
|
65
|
+
weight?: number | undefined;
|
|
66
|
+
}>;
|
|
67
|
+
theme: z.ZodArray<z.ZodObject<{
|
|
68
|
+
name: z.ZodString;
|
|
69
|
+
weight: z.ZodDefault<z.ZodNumber>;
|
|
70
|
+
}, "strip", z.ZodTypeAny, {
|
|
71
|
+
name: string;
|
|
72
|
+
weight: number;
|
|
73
|
+
}, {
|
|
74
|
+
name: string;
|
|
75
|
+
weight?: number | undefined;
|
|
76
|
+
}>, "many">;
|
|
77
|
+
weight: z.ZodDefault<z.ZodNumber>;
|
|
78
|
+
}, "strip", z.ZodTypeAny, {
|
|
79
|
+
weight: number;
|
|
80
|
+
industry: {
|
|
81
|
+
name: string;
|
|
82
|
+
weight: number;
|
|
83
|
+
};
|
|
84
|
+
theme: {
|
|
85
|
+
name: string;
|
|
86
|
+
weight: number;
|
|
87
|
+
}[];
|
|
88
|
+
}, {
|
|
89
|
+
industry: {
|
|
90
|
+
name: string;
|
|
91
|
+
weight?: number | undefined;
|
|
92
|
+
};
|
|
93
|
+
theme: {
|
|
94
|
+
name: string;
|
|
95
|
+
weight?: number | undefined;
|
|
96
|
+
}[];
|
|
97
|
+
weight?: number | undefined;
|
|
98
|
+
}>;
|
|
99
|
+
export type StockClassification = z.infer<typeof StockClassificationSchema>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Stock Classification Result (AI Schema)
|
|
4
|
+
*/
|
|
5
|
+
export const StockClassificationSchema = z.object({
|
|
6
|
+
industry: z.object({
|
|
7
|
+
name: z.string(),
|
|
8
|
+
weight: z.number().min(0).max(100).default(50)
|
|
9
|
+
}).describe("Required main industry category with weight"),
|
|
10
|
+
theme: z.array(z.object({
|
|
11
|
+
name: z.string(),
|
|
12
|
+
weight: z.number().min(0).max(100).default(50)
|
|
13
|
+
})).describe("Thematic categories with weights"),
|
|
14
|
+
weight: z.number().min(0).max(100).default(50).describe("Overall priority weight")
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyDxB;;GAEG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KACjD,CAAC,CAAC,QAAQ,CAAC,6CAA6C,CAAC;IAC1D,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACpB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;KACjD,CAAC,CAAC,CAAC,QAAQ,CAAC,kCAAkC,CAAC;IAChD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,yBAAyB,CAAC;CACrF,CAAC,CAAC"}
|
package/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
2
|
-
import { hedgehogFinancePlugin } from "./src/channel";
|
|
3
|
-
import { setHedgehogRuntime } from "./src/runtime";
|
|
4
|
-
import { allFeaturesTools } from "./src/features";
|
|
2
|
+
import { hedgehogFinancePlugin } from "./src/channel.js";
|
|
3
|
+
import { setHedgehogRuntime } from "./src/runtime.js";
|
|
4
|
+
import { allFeaturesTools } from "./src/features/index.js";
|
|
5
5
|
|
|
6
6
|
export default defineChannelPluginEntry({
|
|
7
|
-
id: "
|
|
7
|
+
id: "hedgehog_finance",
|
|
8
8
|
name: "Hedgehog Finance Comprehensive Plugin",
|
|
9
9
|
description: "WebSocket Channel & Watchlist SQLite Tools for Hedgehog App",
|
|
10
10
|
plugin: hedgehogFinancePlugin,
|
|
@@ -14,6 +14,7 @@ export default defineChannelPluginEntry({
|
|
|
14
14
|
registerFull(api) {
|
|
15
15
|
// 1. 自动化循环注册 Tool
|
|
16
16
|
Object.entries(allFeaturesTools).forEach(([name, tool]) => {
|
|
17
|
+
if (tool.registerTool === false) return;
|
|
17
18
|
const registerable = { ...tool, label: tool.description };
|
|
18
19
|
api.registerTool(registerable as any, { name });
|
|
19
20
|
});
|
|
@@ -21,4 +22,3 @@ export default defineChannelPluginEntry({
|
|
|
21
22
|
api.logger.info("[hedgehog-app] Registered tools and runtime context.");
|
|
22
23
|
},
|
|
23
24
|
});
|
|
24
|
-
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
2
|
+
"id": "hedgehog_finance",
|
|
3
3
|
"kind": "channel",
|
|
4
4
|
"channels": [
|
|
5
|
-
"
|
|
5
|
+
"hedgehog_finance"
|
|
6
6
|
],
|
|
7
7
|
"name": "Hedgehog Finance Comprehensive Plugin",
|
|
8
8
|
"description": "WebSocket Channel & Watchlist SQLite Tools for Hedgehog App",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hedgehog-finance/hedgehog-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "Hedgehog App WebSocket channel and Watchlist Tools for OpenClaw",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bot",
|
|
@@ -11,11 +11,25 @@
|
|
|
11
11
|
"hedgehog-app"
|
|
12
12
|
],
|
|
13
13
|
"repository": "github:hedgehog-finance/hedgehog-plugin",
|
|
14
|
-
"main": "index.
|
|
14
|
+
"main": "dist/index.js",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"author": "Hedgehog Finance",
|
|
17
17
|
"license": "MIT",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist/**/*.js",
|
|
20
|
+
"dist/**/*.js.map",
|
|
21
|
+
"dist/**/*.d.ts",
|
|
22
|
+
"index.ts",
|
|
23
|
+
"src/**/*.ts",
|
|
24
|
+
"openclaw.plugin.json",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"prepack": "npm run build"
|
|
30
|
+
},
|
|
18
31
|
"dependencies": {
|
|
32
|
+
"@mariozechner/pi-ai": "^0.66.1",
|
|
19
33
|
"pino": "^9.0.0",
|
|
20
34
|
"pino-pretty": "^11.1.0",
|
|
21
35
|
"ws": "^8.20.0",
|
|
@@ -31,17 +45,20 @@
|
|
|
31
45
|
"extensions": [
|
|
32
46
|
"./index.ts"
|
|
33
47
|
],
|
|
48
|
+
"runtimeExtensions": [
|
|
49
|
+
"./dist/index.js"
|
|
50
|
+
],
|
|
34
51
|
"install": {
|
|
35
52
|
"npmSpec": "@hedgehog-finance/hedgehog-plugin",
|
|
36
53
|
"defaultChoice": "npm"
|
|
37
54
|
},
|
|
38
55
|
"compat": {
|
|
39
|
-
"pluginApi": ">=2026.4
|
|
40
|
-
"minGatewayVersion": "2026.4
|
|
56
|
+
"pluginApi": ">=2026.5.4",
|
|
57
|
+
"minGatewayVersion": "2026.5.4"
|
|
41
58
|
},
|
|
42
59
|
"build": {
|
|
43
|
-
"openclawVersion": "2026.4
|
|
44
|
-
"pluginSdkVersion": "2026.4
|
|
60
|
+
"openclawVersion": "2026.5.4",
|
|
61
|
+
"pluginSdkVersion": "2026.5.4"
|
|
45
62
|
}
|
|
46
63
|
}
|
|
47
|
-
}
|
|
64
|
+
}
|
package/src/channel.ts
CHANGED
|
@@ -11,13 +11,13 @@ import type {
|
|
|
11
11
|
ChannelAccountSnapshot,
|
|
12
12
|
ChannelStatusIssue
|
|
13
13
|
} from "openclaw/plugin-sdk/channel-contract";
|
|
14
|
-
import { getHedgehogRuntime } from "./runtime";
|
|
15
|
-
import { logger } from "./core/logger";
|
|
14
|
+
import { getHedgehogRuntime } from "./runtime.js";
|
|
15
|
+
import { logger } from "./core/logger.js";
|
|
16
16
|
import type {
|
|
17
17
|
HedgehogFinanceResolvedAccount,
|
|
18
18
|
RelayInboundMessage
|
|
19
|
-
} from "./types";
|
|
20
|
-
import { allFeaturesTools } from "./features";
|
|
19
|
+
} from "./types.js";
|
|
20
|
+
import { allFeaturesTools } from "./features/index.js";
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
|
|
@@ -192,10 +192,10 @@ async function getCurrentTurnUsageAsync(
|
|
|
192
192
|
* Hedgehog Finance Channel Plugin
|
|
193
193
|
*/
|
|
194
194
|
export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount> = {
|
|
195
|
-
id: "
|
|
195
|
+
id: "hedgehog_finance",
|
|
196
196
|
|
|
197
197
|
meta: {
|
|
198
|
-
id: "
|
|
198
|
+
id: "hedgehog_finance",
|
|
199
199
|
label: "Hedgehog Finance",
|
|
200
200
|
selectionLabel: "Hedgehog Finance",
|
|
201
201
|
blurb: "Custom WebSocket relay channel for Hedgehog App",
|
|
@@ -215,7 +215,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
215
215
|
|
|
216
216
|
config: {
|
|
217
217
|
listAccountIds: (cfg: OpenClawConfig): string[] => {
|
|
218
|
-
const channelConfig = (cfg.channels?.['
|
|
218
|
+
const channelConfig = (cfg.channels?.['hedgehog_finance'] || {}) as any;
|
|
219
219
|
|
|
220
220
|
if (channelConfig.accounts) {
|
|
221
221
|
if (Array.isArray(channelConfig.accounts)) {
|
|
@@ -232,7 +232,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
232
232
|
},
|
|
233
233
|
|
|
234
234
|
resolveAccount: (cfg: OpenClawConfig, accountId?: string | null): HedgehogFinanceResolvedAccount => {
|
|
235
|
-
const channelConfig = (cfg.channels?.['
|
|
235
|
+
const channelConfig = (cfg.channels?.['hedgehog_finance'] || {}) as any;
|
|
236
236
|
const id = accountId || channelConfig.accountId || "default";
|
|
237
237
|
|
|
238
238
|
let accountInfo: any;
|
|
@@ -267,7 +267,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
267
267
|
},
|
|
268
268
|
|
|
269
269
|
defaultAccountId: (cfg: OpenClawConfig): string => {
|
|
270
|
-
const channelConfig = (cfg as any)?.channels?.['
|
|
270
|
+
const channelConfig = (cfg as any)?.channels?.['hedgehog_finance'];
|
|
271
271
|
return channelConfig?.accountId || "default";
|
|
272
272
|
},
|
|
273
273
|
},
|
|
@@ -346,6 +346,18 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
346
346
|
if (!method) return;
|
|
347
347
|
|
|
348
348
|
// 从中央工具注册表中查找方法
|
|
349
|
+
if (method === "ping") {
|
|
350
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
351
|
+
ws.send(JSON.stringify({
|
|
352
|
+
type: "res",
|
|
353
|
+
id: id,
|
|
354
|
+
ok: true,
|
|
355
|
+
payload: { success: true }
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
349
361
|
const tool = allFeaturesTools[method];
|
|
350
362
|
|
|
351
363
|
if (tool && typeof tool.execute === 'function') {
|
|
@@ -386,6 +398,16 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
386
398
|
}
|
|
387
399
|
return;
|
|
388
400
|
}
|
|
401
|
+
|
|
402
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
403
|
+
ws.send(JSON.stringify({
|
|
404
|
+
type: "res",
|
|
405
|
+
id: id,
|
|
406
|
+
ok: false,
|
|
407
|
+
error: { message: `Unknown RPC method: ${method}` }
|
|
408
|
+
}));
|
|
409
|
+
}
|
|
410
|
+
return;
|
|
389
411
|
}
|
|
390
412
|
|
|
391
413
|
const { from, text, chatId, id } = appPayload;
|
|
@@ -394,7 +416,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
394
416
|
|
|
395
417
|
const route = rt.channel.routing.resolveAgentRoute({
|
|
396
418
|
cfg,
|
|
397
|
-
channel: "
|
|
419
|
+
channel: "hedgehog_finance",
|
|
398
420
|
accountId: String(accountId),
|
|
399
421
|
peer: { kind: "direct", id: chatId },
|
|
400
422
|
});
|
|
@@ -417,7 +439,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
417
439
|
AccountId: route.accountId,
|
|
418
440
|
AgentId: agentId,
|
|
419
441
|
AgentWorkspace: (route as any).agentWorkspace,
|
|
420
|
-
Provider: "
|
|
442
|
+
Provider: "hedgehog_finance",
|
|
421
443
|
MessageSid: id,
|
|
422
444
|
});
|
|
423
445
|
|
|
@@ -427,7 +449,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
427
449
|
ctx: context,
|
|
428
450
|
updateLastRoute: {
|
|
429
451
|
sessionKey: route.mainSessionKey,
|
|
430
|
-
channel: "
|
|
452
|
+
channel: "hedgehog_finance",
|
|
431
453
|
to: chatId,
|
|
432
454
|
accountId: String(accountId),
|
|
433
455
|
},
|
|
@@ -657,7 +679,7 @@ export const hedgehogFinancePlugin: ChannelPlugin<HedgehogFinanceResolvedAccount
|
|
|
657
679
|
if (!account.configured) {
|
|
658
680
|
return [
|
|
659
681
|
{
|
|
660
|
-
channel: "
|
|
682
|
+
channel: "hedgehog_finance",
|
|
661
683
|
accountId: account.accountId,
|
|
662
684
|
kind: "config" as const,
|
|
663
685
|
message: "Account not configured (missing relay token)",
|
package/src/core/database.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { DatabaseSync } from 'node:sqlite';
|
|
4
4
|
import { mkdirSync, existsSync, copyFileSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { getDbPath, getBackupDir } from "../runtime";
|
|
7
|
-
import { logger } from "./logger";
|
|
6
|
+
import { getDbPath, getBackupDir } from "../runtime.js";
|
|
7
|
+
import { logger } from "./logger.js";
|
|
8
8
|
|
|
9
9
|
let _db: any = null;
|
|
10
10
|
let _backupJobStarted = false;
|
|
@@ -59,6 +59,92 @@ function startDailyBackupJob() {
|
|
|
59
59
|
scheduleNextBackup();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
function normalizeMetadataStockCode(stockCode: string, exchange: string): string {
|
|
63
|
+
const code = String(stockCode || "").trim().toUpperCase();
|
|
64
|
+
if (/\.(SH|SS|SZ|HK|US)$/i.test(code)) {
|
|
65
|
+
return code.replace(/\.SS$/i, ".SH");
|
|
66
|
+
}
|
|
67
|
+
switch (exchange) {
|
|
68
|
+
case "SSE":
|
|
69
|
+
return `${code}.SH`;
|
|
70
|
+
case "SZSE":
|
|
71
|
+
return `${code}.SZ`;
|
|
72
|
+
case "HKEX":
|
|
73
|
+
return `${code}.HK`;
|
|
74
|
+
default:
|
|
75
|
+
return code;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function runWatchlistDedupMigrations(db: DatabaseSync) {
|
|
80
|
+
db.exec("BEGIN TRANSACTION");
|
|
81
|
+
try {
|
|
82
|
+
const metadataRows = db.prepare(`
|
|
83
|
+
SELECT stockCode, exchange FROM global_stock_metadata
|
|
84
|
+
`).all() as { stockCode: string; exchange: string }[];
|
|
85
|
+
const metadataDeleteStmt = db.prepare(`
|
|
86
|
+
DELETE FROM global_stock_metadata WHERE stockCode = ? AND exchange = ?
|
|
87
|
+
`);
|
|
88
|
+
const metadataUpdateStmt = db.prepare(`
|
|
89
|
+
UPDATE global_stock_metadata SET stockCode = ? WHERE stockCode = ? AND exchange = ?
|
|
90
|
+
`);
|
|
91
|
+
const metadataExistsStmt = db.prepare(`
|
|
92
|
+
SELECT 1 FROM global_stock_metadata WHERE stockCode = ? AND exchange = ?
|
|
93
|
+
`);
|
|
94
|
+
for (const row of metadataRows) {
|
|
95
|
+
const normalizedCode = normalizeMetadataStockCode(row.stockCode, row.exchange);
|
|
96
|
+
if (!normalizedCode || normalizedCode === row.stockCode) continue;
|
|
97
|
+
const existing = metadataExistsStmt.get(normalizedCode, row.exchange);
|
|
98
|
+
if (existing) {
|
|
99
|
+
metadataDeleteStmt.run(row.stockCode, row.exchange);
|
|
100
|
+
} else {
|
|
101
|
+
metadataUpdateStmt.run(normalizedCode, row.stockCode, row.exchange);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const duplicateCategories = db.prepare(`
|
|
106
|
+
SELECT userId, name, type, MIN(id) AS keepId, GROUP_CONCAT(id) AS ids
|
|
107
|
+
FROM watchlist_categories
|
|
108
|
+
GROUP BY userId, name, type
|
|
109
|
+
HAVING COUNT(*) > 1
|
|
110
|
+
`).all() as { userId: string; name: string; type: 'industry' | 'theme'; keepId: string; ids: string }[];
|
|
111
|
+
for (const dup of duplicateCategories) {
|
|
112
|
+
const table = dup.type === 'industry' ? 'watchlist_industry_items' : 'watchlist_theme_items';
|
|
113
|
+
const ids = dup.ids.split(",").filter(id => id && id !== dup.keepId);
|
|
114
|
+
for (const oldId of ids) {
|
|
115
|
+
db.prepare(`UPDATE ${table} SET categoryId = ? WHERE userId = ? AND categoryId = ?`).run(dup.keepId, dup.userId, oldId);
|
|
116
|
+
db.prepare("DELETE FROM watchlist_categories WHERE id = ?").run(oldId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
db.exec(`
|
|
121
|
+
DELETE FROM watchlist_industry_items
|
|
122
|
+
WHERE rowid NOT IN (
|
|
123
|
+
SELECT MIN(rowid) FROM watchlist_industry_items GROUP BY watchlistId, categoryId
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
DELETE FROM watchlist_theme_items
|
|
127
|
+
WHERE rowid NOT IN (
|
|
128
|
+
SELECT MIN(rowid) FROM watchlist_theme_items GROUP BY watchlistId, categoryId
|
|
129
|
+
);
|
|
130
|
+
`);
|
|
131
|
+
|
|
132
|
+
db.exec("COMMIT");
|
|
133
|
+
} catch (e) {
|
|
134
|
+
if (db.inTransaction) db.exec("ROLLBACK");
|
|
135
|
+
throw e;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
db.exec(`
|
|
139
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_watchlist_categories_user_name_type
|
|
140
|
+
ON watchlist_categories(userId, name, type);
|
|
141
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_watchlist_industry_items_watchlist_category
|
|
142
|
+
ON watchlist_industry_items(watchlistId, categoryId);
|
|
143
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_watchlist_theme_items_watchlist_category
|
|
144
|
+
ON watchlist_theme_items(watchlistId, categoryId);
|
|
145
|
+
`);
|
|
146
|
+
}
|
|
147
|
+
|
|
62
148
|
export function getDB(): DatabaseSync {
|
|
63
149
|
if (!_db) {
|
|
64
150
|
const dbPath = getDbPath();
|
|
@@ -141,6 +227,7 @@ export function getDB(): DatabaseSync {
|
|
|
141
227
|
CREATE INDEX IF NOT EXISTS idx_watchlist_theme_user ON watchlist_theme_items(userId);
|
|
142
228
|
`);
|
|
143
229
|
|
|
230
|
+
runWatchlistDedupMigrations(_db);
|
|
144
231
|
}
|
|
145
232
|
return _db;
|
|
146
|
-
}
|
|
233
|
+
}
|
package/src/features/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { watchlistTools } from "./watchlist/tools";
|
|
1
|
+
import { watchlistTools } from "./watchlist/tools.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Runtime tool shape used for dynamic RPC dispatch and registerTool.
|
|
@@ -13,6 +13,7 @@ export interface RuntimeTool {
|
|
|
13
13
|
label?: string;
|
|
14
14
|
description: string;
|
|
15
15
|
parameters: unknown;
|
|
16
|
+
registerTool?: boolean;
|
|
16
17
|
// bivariant method signature — allows specific param types
|
|
17
18
|
execute(params: unknown, ctx: { userId: string }): Promise<string>;
|
|
18
19
|
}
|