@hedgehog-finance/hedgehog-plugin 1.0.12 → 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.
Files changed (44) hide show
  1. package/dist/index.d.ts +10 -0
  2. package/dist/index.js +24 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/src/channel.d.ts +6 -0
  5. package/dist/src/channel.js +620 -0
  6. package/dist/src/channel.js.map +1 -0
  7. package/dist/src/core/database.d.ts +2 -0
  8. package/dist/src/core/database.js +220 -0
  9. package/dist/src/core/database.js.map +1 -0
  10. package/dist/src/core/logger.d.ts +3 -0
  11. package/dist/src/core/logger.js +20 -0
  12. package/dist/src/core/logger.js.map +1 -0
  13. package/dist/src/features/index.d.ts +22 -0
  14. package/dist/src/features/index.js +8 -0
  15. package/dist/src/features/index.js.map +1 -0
  16. package/dist/src/features/watchlist/logic.d.ts +48 -0
  17. package/dist/src/features/watchlist/logic.js +607 -0
  18. package/dist/src/features/watchlist/logic.js.map +1 -0
  19. package/dist/src/features/watchlist/schema.d.ts +85 -0
  20. package/dist/src/features/watchlist/schema.js +29 -0
  21. package/dist/src/features/watchlist/schema.js.map +1 -0
  22. package/dist/src/features/watchlist/store.d.ts +1 -0
  23. package/dist/src/features/watchlist/store.js +2 -0
  24. package/dist/src/features/watchlist/store.js.map +1 -0
  25. package/dist/src/features/watchlist/tools.d.ts +135 -0
  26. package/dist/src/features/watchlist/tools.js +572 -0
  27. package/dist/src/features/watchlist/tools.js.map +1 -0
  28. package/dist/src/runtime.d.ts +5 -0
  29. package/dist/src/runtime.js +40 -0
  30. package/dist/src/runtime.js.map +1 -0
  31. package/dist/src/types.d.ts +99 -0
  32. package/dist/src/types.js +16 -0
  33. package/dist/src/types.js.map +1 -0
  34. package/index.ts +4 -4
  35. package/package.json +23 -6
  36. package/src/channel.ts +26 -4
  37. package/src/core/database.ts +90 -3
  38. package/src/features/index.ts +2 -1
  39. package/src/features/watchlist/logic.ts +503 -128
  40. package/src/features/watchlist/schema.ts +1 -6
  41. package/src/features/watchlist/tools.ts +248 -103
  42. package/src/runtime.ts +3 -3
  43. package/src/types.ts +1 -1
  44. 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,7 +1,7 @@
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
7
  id: "hedgehog_finance",
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hedgehog-finance/hedgehog-plugin",
3
- "version": "1.0.12",
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.ts",
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.15",
40
- "minGatewayVersion": "2026.4.15"
56
+ "pluginApi": ">=2026.5.4",
57
+ "minGatewayVersion": "2026.5.4"
41
58
  },
42
59
  "build": {
43
- "openclawVersion": "2026.4.15",
44
- "pluginSdkVersion": "2026.4.15"
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
 
@@ -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;
@@ -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
+ }
@@ -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
  }