@hyperspell/openclaw-hyperspell 0.1.1 → 0.3.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 +33 -15
- package/client.ts +102 -2
- package/commands/setup.ts +536 -0
- package/commands/slash.ts +34 -54
- package/config.ts +75 -3
- package/hooks/memory-sync.ts +63 -0
- package/index.ts +62 -3
- package/openclaw.plugin.json +25 -10
- package/package.json +8 -5
- package/tools/remember.ts +20 -8
- package/tools/search.ts +51 -31
- package/types/openclaw.d.ts +18 -0
package/config.ts
CHANGED
|
@@ -14,6 +14,7 @@ export type HyperspellConfig = {
|
|
|
14
14
|
apiKey: string
|
|
15
15
|
userId?: string
|
|
16
16
|
autoContext: boolean
|
|
17
|
+
syncMemories: boolean
|
|
17
18
|
sources: HyperspellSource[]
|
|
18
19
|
maxResults: number
|
|
19
20
|
debug: boolean
|
|
@@ -23,6 +24,7 @@ const ALLOWED_KEYS = [
|
|
|
23
24
|
"apiKey",
|
|
24
25
|
"userId",
|
|
25
26
|
"autoContext",
|
|
27
|
+
"syncMemories",
|
|
26
28
|
"sources",
|
|
27
29
|
"maxResults",
|
|
28
30
|
"debug",
|
|
@@ -62,8 +64,30 @@ function resolveEnvVars(value: string): string {
|
|
|
62
64
|
})
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
function parseSources(raw: string | undefined): HyperspellSource[] {
|
|
66
|
-
if (!raw
|
|
67
|
+
function parseSources(raw: string | string[] | undefined): HyperspellSource[] {
|
|
68
|
+
if (!raw) {
|
|
69
|
+
return []
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Handle array input
|
|
73
|
+
if (Array.isArray(raw)) {
|
|
74
|
+
const sources = raw
|
|
75
|
+
.map((s) => String(s).trim().toLowerCase())
|
|
76
|
+
.filter((s) => s.length > 0) as HyperspellSource[]
|
|
77
|
+
|
|
78
|
+
for (const source of sources) {
|
|
79
|
+
if (!VALID_SOURCES.includes(source)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Invalid source "${source}". Valid sources: ${VALID_SOURCES.join(", ")}`,
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return sources
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle string input (comma-separated)
|
|
90
|
+
if (typeof raw === "string" && raw.trim() === "") {
|
|
67
91
|
return []
|
|
68
92
|
}
|
|
69
93
|
|
|
@@ -108,7 +132,8 @@ export function parseConfig(raw: unknown): HyperspellConfig {
|
|
|
108
132
|
apiKey,
|
|
109
133
|
userId: cfg.userId as string | undefined,
|
|
110
134
|
autoContext: (cfg.autoContext as boolean) ?? true,
|
|
111
|
-
|
|
135
|
+
syncMemories: (cfg.syncMemories as boolean) ?? false,
|
|
136
|
+
sources: parseSources(cfg.sources as string | string[] | undefined),
|
|
112
137
|
maxResults: (cfg.maxResults as number) ?? 10,
|
|
113
138
|
debug: (cfg.debug as boolean) ?? false,
|
|
114
139
|
}
|
|
@@ -117,3 +142,50 @@ export function parseConfig(raw: unknown): HyperspellConfig {
|
|
|
117
142
|
export const hyperspellConfigSchema = {
|
|
118
143
|
parse: parseConfig,
|
|
119
144
|
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get the workspace directory from OpenClaw config
|
|
148
|
+
*/
|
|
149
|
+
export function getWorkspaceDir(): string {
|
|
150
|
+
const { homedir } = require("node:os")
|
|
151
|
+
const fs = require("node:fs")
|
|
152
|
+
const path = require("node:path")
|
|
153
|
+
|
|
154
|
+
// Resolve config path
|
|
155
|
+
const override = process.env.OPENCLAW_CONFIG_PATH?.trim() || process.env.CLAWDBOT_CONFIG_PATH?.trim()
|
|
156
|
+
let configPath: string
|
|
157
|
+
if (override) {
|
|
158
|
+
configPath = override.startsWith("~")
|
|
159
|
+
? override.replace(/^~(?=$|[\\/])/, homedir())
|
|
160
|
+
: path.resolve(override)
|
|
161
|
+
} else {
|
|
162
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim()
|
|
163
|
+
const resolvedStateDir = stateDir
|
|
164
|
+
? (stateDir.startsWith("~") ? stateDir.replace(/^~(?=$|[\\/])/, homedir()) : path.resolve(stateDir))
|
|
165
|
+
: path.join(homedir(), ".openclaw")
|
|
166
|
+
configPath = path.join(resolvedStateDir, "openclaw.json")
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Read workspace from config
|
|
170
|
+
if (fs.existsSync(configPath)) {
|
|
171
|
+
try {
|
|
172
|
+
const content = fs.readFileSync(configPath, "utf-8")
|
|
173
|
+
const config = JSON.parse(content)
|
|
174
|
+
const workspace = config?.agents?.defaults?.workspace
|
|
175
|
+
if (workspace) {
|
|
176
|
+
return workspace.startsWith("~")
|
|
177
|
+
? workspace.replace(/^~(?=$|[\\/])/, homedir())
|
|
178
|
+
: workspace
|
|
179
|
+
}
|
|
180
|
+
} catch (_e) {
|
|
181
|
+
// Fall back to default
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Default workspace
|
|
186
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR?.trim() || process.env.CLAWDBOT_STATE_DIR?.trim()
|
|
187
|
+
const resolvedStateDir = stateDir
|
|
188
|
+
? (stateDir.startsWith("~") ? stateDir.replace(/^~(?=$|[\\/])/, homedir()) : path.resolve(stateDir))
|
|
189
|
+
: path.join(homedir(), ".openclaw")
|
|
190
|
+
return path.join(resolvedStateDir, "workspace")
|
|
191
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as path from "node:path"
|
|
2
|
+
import type { HyperspellClient } from "../client.ts"
|
|
3
|
+
import type { HyperspellConfig } from "../config.ts"
|
|
4
|
+
import { getWorkspaceDir } from "../config.ts"
|
|
5
|
+
import { log } from "../logger.ts"
|
|
6
|
+
import { syncMarkdownFile, syncAllMemoryFiles } from "../sync/markdown.ts"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build a handler for file change events that syncs markdown files to Hyperspell
|
|
10
|
+
*/
|
|
11
|
+
export function buildFileSyncHandler(client: HyperspellClient, _cfg: HyperspellConfig) {
|
|
12
|
+
const workspaceDir = getWorkspaceDir()
|
|
13
|
+
const memoryDir = path.join(workspaceDir, "memory")
|
|
14
|
+
|
|
15
|
+
return async (event: Record<string, unknown>) => {
|
|
16
|
+
const filePath = event.file_path as string | undefined
|
|
17
|
+
if (!filePath) return
|
|
18
|
+
|
|
19
|
+
// Only process markdown files in the workspace's memory directory
|
|
20
|
+
if (!filePath.startsWith(memoryDir) || !filePath.endsWith(".md")) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const fileName = path.basename(filePath)
|
|
25
|
+
log.info(`Memory file changed: ${fileName}`)
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const result = await syncMarkdownFile(client, filePath)
|
|
29
|
+
if (result.success) {
|
|
30
|
+
log.info(`Synced ${fileName} -> ${result.resourceId}`)
|
|
31
|
+
} else {
|
|
32
|
+
log.error(`Failed to sync ${fileName}: ${result.error}`)
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
log.error(`Error syncing ${fileName}`, err)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sync all existing memory files on startup
|
|
42
|
+
*/
|
|
43
|
+
export async function syncMemoriesOnStartup(
|
|
44
|
+
client: HyperspellClient,
|
|
45
|
+
workspaceDir: string,
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
log.info("Syncing existing memory files...")
|
|
48
|
+
|
|
49
|
+
const result = await syncAllMemoryFiles(client, workspaceDir)
|
|
50
|
+
|
|
51
|
+
if (result.synced > 0) {
|
|
52
|
+
log.info(`Synced ${result.synced} memory files`)
|
|
53
|
+
}
|
|
54
|
+
if (result.failed > 0) {
|
|
55
|
+
log.error(`Failed to sync ${result.failed} files:`)
|
|
56
|
+
for (const error of result.errors) {
|
|
57
|
+
log.error(` - ${error}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (result.synced === 0 && result.failed === 0) {
|
|
61
|
+
log.info("No memory files found in memory/ directory")
|
|
62
|
+
}
|
|
63
|
+
}
|
package/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk"
|
|
2
2
|
import { HyperspellClient } from "./client.ts"
|
|
3
3
|
import { registerCommands } from "./commands/slash.ts"
|
|
4
|
-
import {
|
|
4
|
+
import { registerCliCommands } from "./commands/setup.ts"
|
|
5
|
+
import { parseConfig, hyperspellConfigSchema, getWorkspaceDir } from "./config.ts"
|
|
5
6
|
import { buildAutoContextHandler } from "./hooks/auto-context.ts"
|
|
7
|
+
import { buildFileSyncHandler, syncMemoriesOnStartup } from "./hooks/memory-sync.ts"
|
|
6
8
|
import { initLogger } from "./logger.ts"
|
|
7
9
|
import { registerRememberTool } from "./tools/remember.ts"
|
|
8
10
|
import { registerSearchTool } from "./tools/search.ts"
|
|
@@ -10,11 +12,56 @@ import { registerSearchTool } from "./tools/search.ts"
|
|
|
10
12
|
export default {
|
|
11
13
|
id: "openclaw-hyperspell",
|
|
12
14
|
name: "Hyperspell",
|
|
13
|
-
description: "
|
|
15
|
+
description: "Hyperspell gives your Molty context and memory from all your existing data",
|
|
14
16
|
kind: "memory" as const,
|
|
15
17
|
configSchema: hyperspellConfigSchema,
|
|
16
18
|
|
|
17
19
|
register(api: OpenClawPluginApi) {
|
|
20
|
+
// Register CLI commands (openclaw openclaw-hyperspell setup|status|connect)
|
|
21
|
+
api.registerCli(
|
|
22
|
+
(ctx) => {
|
|
23
|
+
registerCliCommands(ctx.program, api.pluginConfig)
|
|
24
|
+
},
|
|
25
|
+
{ commands: ["openclaw-hyperspell"] },
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// Check if configured
|
|
29
|
+
const rawConfig = api.pluginConfig as Record<string, unknown> | undefined
|
|
30
|
+
const hasConfig = rawConfig?.apiKey || process.env.HYPERSPELL_API_KEY
|
|
31
|
+
|
|
32
|
+
if (!hasConfig) {
|
|
33
|
+
api.logger.info("hyperspell: not configured - run 'openclaw openclaw-hyperspell setup'")
|
|
34
|
+
// Still register slash commands so they show up, but they'll return an error
|
|
35
|
+
api.registerCommand({
|
|
36
|
+
name: "getcontext",
|
|
37
|
+
description: "Search your memories for relevant context",
|
|
38
|
+
acceptsArgs: true,
|
|
39
|
+
requireAuth: false,
|
|
40
|
+
handler: async () => {
|
|
41
|
+
return { text: "Hyperspell not configured. Run 'openclaw openclaw-hyperspell setup' first." }
|
|
42
|
+
},
|
|
43
|
+
})
|
|
44
|
+
api.registerCommand({
|
|
45
|
+
name: "remember",
|
|
46
|
+
description: "Save something to memory",
|
|
47
|
+
acceptsArgs: true,
|
|
48
|
+
requireAuth: false,
|
|
49
|
+
handler: async () => {
|
|
50
|
+
return { text: "Hyperspell not configured. Run 'openclaw openclaw-hyperspell setup' first." }
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
api.registerCommand({
|
|
54
|
+
name: "sync",
|
|
55
|
+
description: "Sync memory/*.md files with Hyperspell",
|
|
56
|
+
acceptsArgs: false,
|
|
57
|
+
requireAuth: false,
|
|
58
|
+
handler: async () => {
|
|
59
|
+
return { text: "Hyperspell not configured. Run 'openclaw openclaw-hyperspell setup' first." }
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
18
65
|
const cfg = parseConfig(api.pluginConfig)
|
|
19
66
|
|
|
20
67
|
initLogger(api.logger, cfg.debug)
|
|
@@ -31,14 +78,26 @@ export default {
|
|
|
31
78
|
api.on("before_agent_start", autoContextHandler)
|
|
32
79
|
}
|
|
33
80
|
|
|
81
|
+
// Register memory sync hook
|
|
82
|
+
if (cfg.syncMemories) {
|
|
83
|
+
const fileSyncHandler = buildFileSyncHandler(client, cfg)
|
|
84
|
+
api.on("file_changed", fileSyncHandler)
|
|
85
|
+
}
|
|
86
|
+
|
|
34
87
|
// Register slash commands
|
|
35
88
|
registerCommands(api, client, cfg)
|
|
36
89
|
|
|
37
90
|
// Register service for lifecycle management
|
|
38
91
|
api.registerService({
|
|
39
92
|
id: "openclaw-hyperspell",
|
|
40
|
-
start: () => {
|
|
93
|
+
start: async () => {
|
|
41
94
|
api.logger.info("hyperspell: connected")
|
|
95
|
+
|
|
96
|
+
// Sync memories on startup if enabled
|
|
97
|
+
if (cfg.syncMemories) {
|
|
98
|
+
const workspaceDir = getWorkspaceDir()
|
|
99
|
+
await syncMemoriesOnStartup(client, workspaceDir)
|
|
100
|
+
}
|
|
42
101
|
},
|
|
43
102
|
stop: () => {
|
|
44
103
|
api.logger.info("hyperspell: stopped")
|
package/openclaw.plugin.json
CHANGED
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
"userId": {
|
|
12
12
|
"label": "User ID",
|
|
13
13
|
"placeholder": "user_123",
|
|
14
|
-
"help": "
|
|
15
|
-
"advanced":
|
|
14
|
+
"help": "User ID (can be your email)",
|
|
15
|
+
"advanced": false
|
|
16
16
|
},
|
|
17
17
|
"autoContext": {
|
|
18
18
|
"label": "Auto-Context",
|
|
19
|
-
"help": "Inject relevant memories before every AI turn"
|
|
19
|
+
"help": "Inject relevant memories before every AI turn",
|
|
20
|
+
"advanced": true
|
|
20
21
|
},
|
|
21
22
|
"sources": {
|
|
22
23
|
"label": "Sources",
|
|
@@ -40,13 +41,27 @@
|
|
|
40
41
|
"type": "object",
|
|
41
42
|
"additionalProperties": false,
|
|
42
43
|
"properties": {
|
|
43
|
-
"apiKey": {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
"apiKey": {
|
|
45
|
+
"type": "string"
|
|
46
|
+
},
|
|
47
|
+
"userId": {
|
|
48
|
+
"type": "string"
|
|
49
|
+
},
|
|
50
|
+
"autoContext": {
|
|
51
|
+
"type": "boolean"
|
|
52
|
+
},
|
|
53
|
+
"sources": {
|
|
54
|
+
"type": "string"
|
|
55
|
+
},
|
|
56
|
+
"maxResults": {
|
|
57
|
+
"type": "number",
|
|
58
|
+
"minimum": 1,
|
|
59
|
+
"maximum": 20
|
|
60
|
+
},
|
|
61
|
+
"debug": {
|
|
62
|
+
"type": "boolean"
|
|
63
|
+
}
|
|
49
64
|
},
|
|
50
65
|
"required": []
|
|
51
66
|
}
|
|
52
|
-
}
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperspell/openclaw-hyperspell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw Hyperspell memory plugin",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
],
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/hyperspell/openclaw-hyperspell.git"
|
|
20
|
+
"url": "git+https://github.com/hyperspell/openclaw-hyperspell.git"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"openclaw",
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
"plugin"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"@sinclair/typebox": "^0.34.0"
|
|
31
|
+
"@clack/prompts": "^1.0.0",
|
|
32
|
+
"@sinclair/typebox": "^0.34.0",
|
|
33
|
+
"hyperspell": "^0.30.0"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
35
36
|
"openclaw": ">=2026.1.29"
|
|
@@ -40,7 +41,9 @@
|
|
|
40
41
|
"lint:fix": "bunx @biomejs/biome check --write ."
|
|
41
42
|
},
|
|
42
43
|
"openclaw": {
|
|
43
|
-
"extensions": [
|
|
44
|
+
"extensions": [
|
|
45
|
+
"./index.ts"
|
|
46
|
+
]
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"typescript": "^5.9.3"
|
package/tools/remember.ts
CHANGED
|
@@ -26,16 +26,28 @@ export function registerRememberTool(
|
|
|
26
26
|
) {
|
|
27
27
|
log.debug(`remember tool: "${params.text.slice(0, 50)}..."`)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
try {
|
|
30
|
+
await client.addMemory(params.text, {
|
|
31
|
+
title: params.title,
|
|
32
|
+
metadata: { source: "openclaw_tool" },
|
|
33
|
+
})
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
const preview =
|
|
36
|
+
params.text.length > 80 ? `${params.text.slice(0, 80)}…` : params.text
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text" as const, text: `Stored: "${preview}"` }],
|
|
40
|
+
}
|
|
41
|
+
} catch (err) {
|
|
42
|
+
log.error("remember tool failed", err)
|
|
43
|
+
return {
|
|
44
|
+
content: [
|
|
45
|
+
{
|
|
46
|
+
type: "text" as const,
|
|
47
|
+
text: `Failed to store memory: ${err instanceof Error ? err.message : String(err)}`,
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
}
|
|
39
51
|
}
|
|
40
52
|
},
|
|
41
53
|
},
|
package/tools/search.ts
CHANGED
|
@@ -28,42 +28,62 @@ export function registerSearchTool(
|
|
|
28
28
|
const limit = params.limit ?? 5
|
|
29
29
|
log.debug(`search tool: query="${params.query}" limit=${limit}`)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
try {
|
|
32
|
+
const response = await client.searchRaw(params.query, { limit })
|
|
33
|
+
const documents = (response.documents ?? []) as Array<{
|
|
34
|
+
source: string
|
|
35
|
+
resource_id: string
|
|
36
|
+
score?: number
|
|
37
|
+
summary?: string
|
|
38
|
+
title?: string
|
|
39
|
+
metadata?: Record<string, unknown>
|
|
40
|
+
highlights?: Array<{ text: string }>
|
|
41
|
+
data?: Array<{ text: string }>
|
|
42
|
+
}>
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
if (documents.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{ type: "text" as const, text: "No relevant memories found." },
|
|
48
|
+
],
|
|
49
|
+
}
|
|
38
50
|
}
|
|
39
|
-
}
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
const formattedDocs = documents
|
|
53
|
+
.map((doc, i) => {
|
|
54
|
+
const relevance = doc.score
|
|
55
|
+
? `${Math.round(doc.score * 100)}%`
|
|
56
|
+
: "N/A"
|
|
57
|
+
const title = doc.title || "(untitled)"
|
|
58
|
+
const summary = doc.summary || "(no summary)"
|
|
59
|
+
return `${i + 1}. Source: ${doc.source}\n Title: ${title}\n Summary: ${summary}\n Relevance: ${relevance}`
|
|
60
|
+
})
|
|
61
|
+
.join("\n\n")
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
const text = `Found ${documents.length} memories:\n\n${formattedDocs}`
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text" as const,
|
|
69
|
+
text,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
details: {
|
|
73
|
+
count: documents.length,
|
|
74
|
+
documents,
|
|
56
75
|
},
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
log.error("search tool failed", err)
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: "text" as const,
|
|
83
|
+
text: `Search failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
}
|
|
67
87
|
}
|
|
68
88
|
},
|
|
69
89
|
},
|
package/types/openclaw.d.ts
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
declare module "openclaw/plugin-sdk" {
|
|
2
|
+
import type { Command } from "commander"
|
|
3
|
+
|
|
4
|
+
export interface OpenClawPluginCliContext {
|
|
5
|
+
program: Command
|
|
6
|
+
config: unknown
|
|
7
|
+
workspaceDir?: string
|
|
8
|
+
logger: {
|
|
9
|
+
info: (message: string, ...args: unknown[]) => void
|
|
10
|
+
warn: (message: string, ...args: unknown[]) => void
|
|
11
|
+
error: (message: string, ...args: unknown[]) => void
|
|
12
|
+
debug: (message: string, ...args: unknown[]) => void
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type OpenClawPluginCliRegistrar = (ctx: OpenClawPluginCliContext) => void | Promise<void>
|
|
17
|
+
|
|
2
18
|
export interface OpenClawPluginApi {
|
|
3
19
|
pluginConfig: unknown
|
|
20
|
+
workspaceDir?: string
|
|
4
21
|
logger: {
|
|
5
22
|
info: (message: string, ...args: unknown[]) => void
|
|
6
23
|
warn: (message: string, ...args: unknown[]) => void
|
|
@@ -14,6 +31,7 @@ declare module "openclaw/plugin-sdk" {
|
|
|
14
31
|
requireAuth: boolean
|
|
15
32
|
handler: (ctx: { args?: string; senderId?: string; channel?: string }) => Promise<{ text: string }>
|
|
16
33
|
}): void
|
|
34
|
+
registerCli(registrar: OpenClawPluginCliRegistrar, opts?: { commands?: string[] }): void
|
|
17
35
|
registerTool<T = unknown>(
|
|
18
36
|
options: {
|
|
19
37
|
name: string
|