@hyperspell/openclaw-hyperspell 0.4.2 → 0.7.1
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 +127 -3
- package/client.ts +311 -202
- package/commands/setup.ts +190 -55
- package/commands/wine.ts +164 -0
- package/config.ts +244 -162
- package/graph/cron.ts +364 -0
- package/graph/index.ts +5 -0
- package/graph/ops.ts +253 -0
- package/graph/state.ts +79 -0
- package/graph/tools.ts +117 -0
- package/hooks/auto-context.ts +17 -11
- package/hooks/auto-trace.ts +127 -0
- package/index.ts +123 -95
- package/lib/browser.ts +10 -6
- package/lib/run-script.ts +32 -0
- package/openclaw.plugin.json +110 -74
- package/package.json +6 -2
- package/sommeliagent/references/cross-domain-mappings.md +63 -0
- package/sommeliagent/scripts/auth.py +222 -0
- package/sommeliagent/scripts/history.py +72 -0
- package/sommeliagent/scripts/rate.py +76 -0
- package/sommeliagent/scripts/recommend.py +777 -0
- package/sommeliagent/scripts/test_recommend.py +459 -0
- package/sommeliagent/scripts/wine_db.py +3224 -0
- package/tools/remember.ts +6 -2
- package/tools/search.ts +9 -3
- package/tools/sommelier.ts +254 -0
package/config.ts
CHANGED
|
@@ -1,191 +1,273 @@
|
|
|
1
1
|
export type HyperspellSource =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
| "collections"
|
|
3
|
+
| "reddit"
|
|
4
|
+
| "notion"
|
|
5
|
+
| "slack"
|
|
6
|
+
| "google_calendar"
|
|
7
|
+
| "google_mail"
|
|
8
|
+
| "box"
|
|
9
|
+
| "google_drive"
|
|
10
|
+
| "vault"
|
|
11
|
+
| "web_crawler";
|
|
12
|
+
|
|
13
|
+
export type KnowledgeGraphConfig = {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
scanIntervalMinutes: number;
|
|
16
|
+
batchSize: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type AutoTraceConfig = {
|
|
20
|
+
enabled: boolean;
|
|
21
|
+
extract: Array<"procedure" | "memory" | "mood">;
|
|
22
|
+
metadata?: Record<string, string | number | boolean>;
|
|
23
|
+
};
|
|
12
24
|
|
|
13
25
|
export type HyperspellConfig = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
apiKey: string;
|
|
27
|
+
userId?: string;
|
|
28
|
+
autoContext: boolean;
|
|
29
|
+
autoTrace: AutoTraceConfig;
|
|
30
|
+
syncMemories: boolean;
|
|
31
|
+
sources: HyperspellSource[];
|
|
32
|
+
maxResults: number;
|
|
33
|
+
relevanceThreshold: number;
|
|
34
|
+
debug: boolean;
|
|
35
|
+
knowledgeGraph: KnowledgeGraphConfig;
|
|
36
|
+
};
|
|
22
37
|
|
|
23
38
|
const ALLOWED_KEYS = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
"apiKey",
|
|
40
|
+
"userId",
|
|
41
|
+
"autoContext",
|
|
42
|
+
"autoTrace",
|
|
43
|
+
"syncMemories",
|
|
44
|
+
"sources",
|
|
45
|
+
"maxResults",
|
|
46
|
+
"relevanceThreshold",
|
|
47
|
+
"debug",
|
|
48
|
+
"knowledgeGraph",
|
|
49
|
+
];
|
|
32
50
|
|
|
33
51
|
const VALID_SOURCES: HyperspellSource[] = [
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
]
|
|
52
|
+
"collections",
|
|
53
|
+
"reddit",
|
|
54
|
+
"notion",
|
|
55
|
+
"slack",
|
|
56
|
+
"google_calendar",
|
|
57
|
+
"google_mail",
|
|
58
|
+
"box",
|
|
59
|
+
"google_drive",
|
|
60
|
+
"vault",
|
|
61
|
+
"web_crawler",
|
|
62
|
+
];
|
|
45
63
|
|
|
46
64
|
function assertAllowedKeys(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
value: Record<string, unknown>,
|
|
66
|
+
allowed: string[],
|
|
67
|
+
label: string,
|
|
50
68
|
): void {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
const unknown = Object.keys(value).filter((k) => !allowed.includes(k));
|
|
70
|
+
if (unknown.length > 0) {
|
|
71
|
+
throw new Error(`${label} has unknown keys: ${unknown.join(", ")}`);
|
|
72
|
+
}
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
function resolveEnvVars(value: string): string {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
return value.replace(/\$\{([^}]+)\}/g, (_, envVar: string) => {
|
|
77
|
+
const envValue = process.env[envVar];
|
|
78
|
+
if (!envValue) {
|
|
79
|
+
throw new Error(`Environment variable ${envVar} is not set`);
|
|
80
|
+
}
|
|
81
|
+
return envValue;
|
|
82
|
+
});
|
|
65
83
|
}
|
|
66
84
|
|
|
67
85
|
function parseSources(raw: string | string[] | undefined): HyperspellSource[] {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
86
|
+
if (!raw) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Handle array input
|
|
91
|
+
if (Array.isArray(raw)) {
|
|
92
|
+
const sources = raw
|
|
93
|
+
.map((s) => String(s).trim().toLowerCase())
|
|
94
|
+
.filter((s) => s.length > 0) as HyperspellSource[];
|
|
95
|
+
|
|
96
|
+
for (const source of sources) {
|
|
97
|
+
if (!VALID_SOURCES.includes(source)) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Invalid source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return sources;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Handle string input (comma-separated)
|
|
108
|
+
if (typeof raw === "string" && raw.trim() === "") {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const sources = raw
|
|
113
|
+
.split(",")
|
|
114
|
+
.map((s) => s.trim().toLowerCase())
|
|
115
|
+
.filter((s) => s.length > 0) as HyperspellSource[];
|
|
116
|
+
|
|
117
|
+
for (const source of sources) {
|
|
118
|
+
if (!VALID_SOURCES.includes(source)) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Invalid source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return sources;
|
|
108
126
|
}
|
|
109
127
|
|
|
110
128
|
export function parseConfig(raw: unknown): HyperspellConfig {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
const cfg =
|
|
130
|
+
raw && typeof raw === "object" && !Array.isArray(raw)
|
|
131
|
+
? (raw as Record<string, unknown>)
|
|
132
|
+
: {};
|
|
133
|
+
|
|
134
|
+
if (Object.keys(cfg).length > 0) {
|
|
135
|
+
assertAllowedKeys(cfg, ALLOWED_KEYS, "hyperspell config");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const apiKey =
|
|
139
|
+
typeof cfg.apiKey === "string" && cfg.apiKey.length > 0
|
|
140
|
+
? resolveEnvVars(cfg.apiKey)
|
|
141
|
+
: process.env.HYPERSPELL_API_KEY;
|
|
142
|
+
|
|
143
|
+
if (!apiKey) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
"hyperspell: apiKey is required (set in plugin config or HYPERSPELL_API_KEY env var)",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const kgRaw = (cfg.knowledgeGraph ?? {}) as Record<string, unknown>;
|
|
150
|
+
const atRaw = (cfg.autoTrace ?? {}) as Record<string, unknown>;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
apiKey,
|
|
154
|
+
userId: cfg.userId as string | undefined,
|
|
155
|
+
autoContext: (cfg.autoContext as boolean) ?? true,
|
|
156
|
+
autoTrace: {
|
|
157
|
+
enabled: (atRaw.enabled as boolean) ?? false,
|
|
158
|
+
extract: (atRaw.extract as Array<"procedure" | "memory" | "mood">) ?? [
|
|
159
|
+
"procedure",
|
|
160
|
+
],
|
|
161
|
+
metadata: atRaw.metadata as
|
|
162
|
+
| Record<string, string | number | boolean>
|
|
163
|
+
| undefined,
|
|
164
|
+
},
|
|
165
|
+
syncMemories: (cfg.syncMemories as boolean) ?? false,
|
|
166
|
+
sources: parseSources(cfg.sources as string | string[] | undefined),
|
|
167
|
+
maxResults: (cfg.maxResults as number) ?? 10,
|
|
168
|
+
relevanceThreshold: (cfg.relevanceThreshold as number) ?? 0.6,
|
|
169
|
+
debug: (cfg.debug as boolean) ?? false,
|
|
170
|
+
knowledgeGraph: {
|
|
171
|
+
enabled: (kgRaw.enabled as boolean) ?? false,
|
|
172
|
+
scanIntervalMinutes: (kgRaw.scanIntervalMinutes as number) ?? 60,
|
|
173
|
+
batchSize: (kgRaw.batchSize as number) ?? 20,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
140
176
|
}
|
|
141
177
|
|
|
142
178
|
export const hyperspellConfigSchema = {
|
|
143
|
-
|
|
179
|
+
parse: parseConfig,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Resolve OpenClaw state directory (matches OpenClaw's own logic).
|
|
184
|
+
*/
|
|
185
|
+
export function resolveStateDir(): string {
|
|
186
|
+
const { homedir } = require("node:os");
|
|
187
|
+
const path = require("node:path");
|
|
188
|
+
|
|
189
|
+
const override =
|
|
190
|
+
process.env.OPENCLAW_STATE_DIR?.trim() ||
|
|
191
|
+
process.env.CLAWDBOT_STATE_DIR?.trim();
|
|
192
|
+
if (override) {
|
|
193
|
+
return override.startsWith("~")
|
|
194
|
+
? override.replace(/^~(?=$|[\\/])/, homedir())
|
|
195
|
+
: path.resolve(override);
|
|
196
|
+
}
|
|
197
|
+
return path.join(homedir(), ".openclaw");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Resolve OpenClaw config file path (matches OpenClaw's own logic).
|
|
202
|
+
*/
|
|
203
|
+
export function resolveConfigPath(): string {
|
|
204
|
+
const path = require("node:path");
|
|
205
|
+
|
|
206
|
+
const override =
|
|
207
|
+
process.env.OPENCLAW_CONFIG_PATH?.trim() ||
|
|
208
|
+
process.env.CLAWDBOT_CONFIG_PATH?.trim();
|
|
209
|
+
if (override) {
|
|
210
|
+
const { homedir } = require("node:os");
|
|
211
|
+
return override.startsWith("~")
|
|
212
|
+
? override.replace(/^~(?=$|[\\/])/, homedir())
|
|
213
|
+
: path.resolve(override);
|
|
214
|
+
}
|
|
215
|
+
return path.join(resolveStateDir(), "openclaw.json");
|
|
144
216
|
}
|
|
145
217
|
|
|
146
218
|
/**
|
|
147
219
|
* Get the workspace directory from OpenClaw config
|
|
148
220
|
*/
|
|
149
221
|
export function getWorkspaceDir(): string {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
222
|
+
const { homedir } = require("node:os");
|
|
223
|
+
const fs = require("node:fs");
|
|
224
|
+
const path = require("node:path");
|
|
225
|
+
|
|
226
|
+
// Resolve config path
|
|
227
|
+
const override =
|
|
228
|
+
process.env.OPENCLAW_CONFIG_PATH?.trim() ||
|
|
229
|
+
process.env.CLAWDBOT_CONFIG_PATH?.trim();
|
|
230
|
+
let configPath: string;
|
|
231
|
+
if (override) {
|
|
232
|
+
configPath = override.startsWith("~")
|
|
233
|
+
? override.replace(/^~(?=$|[\\/])/, homedir())
|
|
234
|
+
: path.resolve(override);
|
|
235
|
+
} else {
|
|
236
|
+
const stateDir =
|
|
237
|
+
process.env.OPENCLAW_STATE_DIR?.trim() ||
|
|
238
|
+
process.env.CLAWDBOT_STATE_DIR?.trim();
|
|
239
|
+
const resolvedStateDir = stateDir
|
|
240
|
+
? stateDir.startsWith("~")
|
|
241
|
+
? stateDir.replace(/^~(?=$|[\\/])/, homedir())
|
|
242
|
+
: path.resolve(stateDir)
|
|
243
|
+
: path.join(homedir(), ".openclaw");
|
|
244
|
+
configPath = path.join(resolvedStateDir, "openclaw.json");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Read workspace from config
|
|
248
|
+
if (fs.existsSync(configPath)) {
|
|
249
|
+
try {
|
|
250
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
251
|
+
const config = JSON.parse(content);
|
|
252
|
+
const workspace = config?.agents?.defaults?.workspace;
|
|
253
|
+
if (workspace) {
|
|
254
|
+
return workspace.startsWith("~")
|
|
255
|
+
? workspace.replace(/^~(?=$|[\\/])/, homedir())
|
|
256
|
+
: workspace;
|
|
257
|
+
}
|
|
258
|
+
} catch (_e) {
|
|
259
|
+
// Fall back to default
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Default workspace
|
|
264
|
+
const stateDir =
|
|
265
|
+
process.env.OPENCLAW_STATE_DIR?.trim() ||
|
|
266
|
+
process.env.CLAWDBOT_STATE_DIR?.trim();
|
|
267
|
+
const resolvedStateDir = stateDir
|
|
268
|
+
? stateDir.startsWith("~")
|
|
269
|
+
? stateDir.replace(/^~(?=$|[\\/])/, homedir())
|
|
270
|
+
: path.resolve(stateDir)
|
|
271
|
+
: path.join(homedir(), ".openclaw");
|
|
272
|
+
return path.join(resolvedStateDir, "workspace");
|
|
191
273
|
}
|