@dn-inc/openclaw-seahorse 0.2.0 → 0.2.2
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 +79 -64
- package/index.ts +1 -1
- package/package.json +1 -1
- package/src/helpers.ts +16 -6
- package/src/service.ts +2 -1
- package/src/tools.ts +36 -13
- package/src/types.ts +15 -0
package/README.md
CHANGED
|
@@ -3,17 +3,23 @@
|
|
|
3
3
|
Drop-in replacement for OpenClaw's built-in memory search, powered by [Seahorse](https://seahorse.dnotitia.ai).
|
|
4
4
|
|
|
5
5
|
- Uploads local memory files (`MEMORY.md`, `memory/**/*.md`) to Seahorse Storage
|
|
6
|
-
-
|
|
6
|
+
- Syncs the entire workspace for full semantic search (`syncWorkspace: true`)
|
|
7
7
|
- Routes `memory_search` to Seahorse's vector search API
|
|
8
8
|
- Keeps `memory_get` reading from local files (same as native)
|
|
9
9
|
|
|
10
|
+
## Why Seahorse?
|
|
11
|
+
|
|
12
|
+
OpenClaw's built-in memory only indexes markdown notes the agent explicitly writes (`MEMORY.md` and `memory/**/*.md`). But agents produce far more than markdown — JSON data, YAML configs, PDF reports, images, spreadsheets, and other artifacts accumulate in the workspace over time, invisible to memory search.
|
|
13
|
+
|
|
14
|
+
With `syncWorkspace: true`, Seahorse indexes every supported file in the workspace automatically. The agent can semantically search across all of its own output — not just what it remembered to write down. Documents, data files, images, and markup (37 supported extensions) all become searchable through the same `memory_search` tool, with no API or workflow changes.
|
|
15
|
+
|
|
10
16
|
## Requirements
|
|
11
17
|
|
|
12
18
|
- Node.js 20+
|
|
13
19
|
- OpenClaw >= 2026.1.26
|
|
14
20
|
- Seahorse account (Storage + Table tenants)
|
|
15
21
|
|
|
16
|
-
## Quick Start
|
|
22
|
+
## Quick Start
|
|
17
23
|
|
|
18
24
|
1. Install the plugin:
|
|
19
25
|
|
|
@@ -35,9 +41,76 @@ openclaw plugins install @dn-inc/openclaw-seahorse
|
|
|
35
41
|
}
|
|
36
42
|
```
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
The `apiKey` field supports `${ENV_VAR}` syntax (e.g., `"${SEAHORSE_API_KEY}"`), or you can omit it entirely and set the `SEAHORSE_API_KEY` environment variable.
|
|
45
|
+
|
|
46
|
+
That's it. The plugin auto-syncs your files on startup and watches for changes in real time.
|
|
47
|
+
|
|
48
|
+
## File Sync
|
|
49
|
+
|
|
50
|
+
The plugin runs a background service (`seahorse-sync`) that keeps Seahorse Storage in sync:
|
|
51
|
+
|
|
52
|
+
- **Initial sync**: scans files on startup, uploads only changed files
|
|
53
|
+
- **Live watch**: detects file create/modify/delete via `fs.watch` (1.5s debounce)
|
|
54
|
+
- **Dedup**: mtime-based state tracking prevents redundant uploads
|
|
55
|
+
- **Delete sync**: local file deletion triggers Seahorse Storage deletion
|
|
56
|
+
|
|
57
|
+
By default, only memory files (`MEMORY.md` + `memory/**/*.md`) are synced. Set `syncWorkspace: true` to sync the entire workspace — all supported files become searchable via `memory_search` without any tool or API changes.
|
|
58
|
+
|
|
59
|
+
Supported file extensions (workspace sync):
|
|
39
60
|
|
|
40
|
-
|
|
61
|
+
| Category | Extensions |
|
|
62
|
+
|----------|-----------|
|
|
63
|
+
| Documents | `.doc`, `.docx`, `.hwp`, `.hwpx`, `.odg`, `.odp`, `.odt`, `.pdf`, `.ppt`, `.pptx`, `.rtf` |
|
|
64
|
+
| Data | `.csv`, `.json`, `.ods`, `.toml`, `.tsv`, `.xls`, `.xlsx`, `.yaml`, `.yml` |
|
|
65
|
+
| Images | `.bmp`, `.gif`, `.jpeg`, `.jpg`, `.png`, `.tif`, `.tiff`, `.webp` |
|
|
66
|
+
| Text / Markup | `.epub`, `.htm`, `.html`, `.markdown`, `.md`, `.text`, `.txt`, `.xhtml`, `.xml` |
|
|
67
|
+
|
|
68
|
+
When workspace sync is enabled, dotfiles/dotdirs (`.*`) are always excluded. Add further patterns via `syncBlacklist`:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"storageUrl": "...", "tableUrl": "...", "apiKey": "...",
|
|
73
|
+
"syncWorkspace": true,
|
|
74
|
+
"syncBlacklist": ["node_modules/", "dist/", "*.log", "*.tmp"]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Supported blacklist patterns:
|
|
79
|
+
|
|
80
|
+
| Pattern | Example | Matches |
|
|
81
|
+
|---------|---------|---------|
|
|
82
|
+
| `"dir/"` | `"node_modules/"` | Directory with that exact name |
|
|
83
|
+
| `"*.ext"` | `"*.tmp"` | Any segment ending with `.tmp` |
|
|
84
|
+
| `"prefix*"` | `".*"` | Any segment starting with `.` (built-in) |
|
|
85
|
+
| `"name"` | `"Thumbs.db"` | Exact filename in any directory |
|
|
86
|
+
|
|
87
|
+
## Storage Layout
|
|
88
|
+
|
|
89
|
+
All synced files are stored under the `openclaw/` prefix in Seahorse Storage (e.g., `MEMORY.md` → `openclaw/MEMORY.md`). This isolates plugin data from other data in the same Seahorse tenant. Search results are mapped back to workspace-relative paths automatically.
|
|
90
|
+
|
|
91
|
+
## Tools
|
|
92
|
+
|
|
93
|
+
### `memory_search`
|
|
94
|
+
|
|
95
|
+
Semantic vector search over synced files via Seahorse.
|
|
96
|
+
|
|
97
|
+
| Parameter | Type | Required | Description |
|
|
98
|
+
|-----------|------|----------|-------------|
|
|
99
|
+
| `query` | string | Yes | Search query |
|
|
100
|
+
| `maxResults` | number | No | Max results (default: `topK` config) |
|
|
101
|
+
| `minScore` | number | No | Min relevance score 0-1 (default: `minScore` config) |
|
|
102
|
+
|
|
103
|
+
### `memory_get`
|
|
104
|
+
|
|
105
|
+
Read a local file by path.
|
|
106
|
+
|
|
107
|
+
| Parameter | Type | Required | Description |
|
|
108
|
+
|-----------|------|----------|-------------|
|
|
109
|
+
| `path` | string | Yes | Relative path (e.g., `MEMORY.md`, `memory/notes.md`) |
|
|
110
|
+
| `from` | number | No | Start line (1-based) |
|
|
111
|
+
| `lines` | number | No | Number of lines to read |
|
|
112
|
+
|
|
113
|
+
By default, allowed paths are `MEMORY.md` and files under `memory/` only. With `syncWorkspace: true`, any workspace file is accessible (except blacklisted paths).
|
|
41
114
|
|
|
42
115
|
## Config Reference
|
|
43
116
|
|
|
@@ -47,7 +120,7 @@ That's it. The plugin auto-syncs your files on startup and watches for changes i
|
|
|
47
120
|
|-------|------|----------|-------------|
|
|
48
121
|
| `storageUrl` | string | Yes | Seahorse Storage tenant URL |
|
|
49
122
|
| `tableUrl` | string | Yes | Seahorse Table tenant URL |
|
|
50
|
-
| `apiKey` | string | No | API key. Auto-reads from `SEAHORSE_API_KEY` env var if omitted.
|
|
123
|
+
| `apiKey` | string | No | API key. Auto-reads from `SEAHORSE_API_KEY` env var if omitted. Supports `${ENV_VAR}` syntax. |
|
|
51
124
|
|
|
52
125
|
### Layer 1: Search & Sync Behavior (optional)
|
|
53
126
|
|
|
@@ -125,7 +198,7 @@ Example — hybrid with equal dense/sparse weighting (Seahorse native behavior):
|
|
|
125
198
|
|
|
126
199
|
## Defaults Comparison: OpenClaw vs Seahorse
|
|
127
200
|
|
|
128
|
-
The plugin ships with OpenClaw-compatible defaults
|
|
201
|
+
The plugin ships with OpenClaw-compatible defaults. The table below shows where these differ from Seahorse server defaults, so you can tune toward Seahorse-native behavior when desired.
|
|
129
202
|
|
|
130
203
|
| Parameter | Plugin Default | OpenClaw Native | Seahorse Server | Notes |
|
|
131
204
|
|-----------|---------------|-----------------|-----------------|-------|
|
|
@@ -142,61 +215,3 @@ The plugin ships with OpenClaw-compatible defaults so it works as a drop-in repl
|
|
|
142
215
|
| Projection | `"id, text, metadata, distance, score"` | N/A | *(must specify)* | Requests both `distance` (dense) and `score` (hybrid) for proper normalization |
|
|
143
216
|
|
|
144
217
|
> **Hybrid (RRF) Score Normalization**: In hybrid mode, Seahorse returns raw RRF scores (~0.01-0.03). The plugin normalizes these to a 0-1 scale by dividing by the theoretical maximum (`numRankers / (k + 1)`), making them compatible with the `minScore` threshold. With default k=60 and 2 rankers, a raw score of 0.016 becomes ~0.49 after normalization.
|
|
145
|
-
|
|
146
|
-
## Tools
|
|
147
|
-
|
|
148
|
-
### `memory_search`
|
|
149
|
-
|
|
150
|
-
Semantic vector search over synced files via Seahorse.
|
|
151
|
-
|
|
152
|
-
| Parameter | Type | Required | Description |
|
|
153
|
-
|-----------|------|----------|-------------|
|
|
154
|
-
| `query` | string | Yes | Search query |
|
|
155
|
-
| `maxResults` | number | No | Max results (default: `topK` config) |
|
|
156
|
-
| `minScore` | number | No | Min relevance score 0-1 (default: `minScore` config) |
|
|
157
|
-
|
|
158
|
-
### `memory_get`
|
|
159
|
-
|
|
160
|
-
Read a local memory file by path.
|
|
161
|
-
|
|
162
|
-
| Parameter | Type | Required | Description |
|
|
163
|
-
|-----------|------|----------|-------------|
|
|
164
|
-
| `path` | string | Yes | Relative path (e.g., `MEMORY.md`, `memory/notes.md`) |
|
|
165
|
-
| `from` | number | No | Start line (1-based) |
|
|
166
|
-
| `lines` | number | No | Number of lines to read |
|
|
167
|
-
|
|
168
|
-
Allowed paths: `MEMORY.md` (workspace root) and files under `memory/` only.
|
|
169
|
-
|
|
170
|
-
## Storage Layout
|
|
171
|
-
|
|
172
|
-
All memory files are stored under the `openclaw/` prefix in Seahorse Storage (e.g., `MEMORY.md` → `openclaw/MEMORY.md`). This isolates OpenClaw memory files from other data in the same tenant. Search results are mapped back to workspace-relative paths automatically.
|
|
173
|
-
|
|
174
|
-
## File Sync
|
|
175
|
-
|
|
176
|
-
The plugin runs a background service (`seahorse-sync`) that keeps Seahorse Storage in sync:
|
|
177
|
-
|
|
178
|
-
- **Initial sync**: scans files on startup, uploads only changed files
|
|
179
|
-
- **Live watch**: detects file create/modify/delete via `fs.watch` (1.5s debounce)
|
|
180
|
-
- **Dedup**: mtime-based state tracking prevents redundant uploads
|
|
181
|
-
- **Delete sync**: local file deletion triggers Seahorse Storage deletion
|
|
182
|
-
|
|
183
|
-
By default, only memory files (`MEMORY.md` + `memory/**/*.md`) are synced. Set `syncWorkspace: true` to sync the entire workspace — all files become searchable via `memory_search` without any tool or API changes.
|
|
184
|
-
|
|
185
|
-
When workspace sync is enabled, dotfiles/dotdirs (`.*`) are always excluded. Add further patterns via `syncBlacklist`:
|
|
186
|
-
|
|
187
|
-
```json
|
|
188
|
-
{
|
|
189
|
-
"storageUrl": "...", "tableUrl": "...", "apiKey": "...",
|
|
190
|
-
"syncWorkspace": true,
|
|
191
|
-
"syncBlacklist": ["node_modules/", "dist/", "*.log", "*.tmp"]
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
Supported blacklist patterns:
|
|
196
|
-
|
|
197
|
-
| Pattern | Example | Matches |
|
|
198
|
-
|---------|---------|---------|
|
|
199
|
-
| `"dir/"` | `"node_modules/"` | Directory with that exact name |
|
|
200
|
-
| `"*.ext"` | `"*.tmp"` | Any segment ending with `.tmp` |
|
|
201
|
-
| `"prefix*"` | `".*"` | Any segment starting with `.` (built-in) |
|
|
202
|
-
| `"name"` | `"Thumbs.db"` | Exact filename in any directory |
|
package/index.ts
CHANGED
|
@@ -94,7 +94,7 @@ const plugin: OpenClawPluginDefinition = {
|
|
|
94
94
|
const client = new SeahorseClient(config, api.logger);
|
|
95
95
|
|
|
96
96
|
registerMemorySearchTool(api, config, client);
|
|
97
|
-
registerMemoryGetTool(api);
|
|
97
|
+
registerMemoryGetTool(api, config);
|
|
98
98
|
registerSyncService(api, client, config);
|
|
99
99
|
|
|
100
100
|
api.logger.info("[openclaw-seahorse] Plugin registered (memory_search, memory_get, sync service).");
|
package/package.json
CHANGED
package/src/helpers.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { SUPPORTED_EXTENSIONS } from "./types.js";
|
|
3
4
|
|
|
4
5
|
/** Resolve ${ENV_VAR} patterns in a string to process.env values. */
|
|
5
6
|
export function resolveEnvVars(value: string): string {
|
|
@@ -103,6 +104,12 @@ export function parseUnitInterval(raw: unknown, name: string): number | undefine
|
|
|
103
104
|
return n;
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
/** Check whether a file path has a Seahorse-supported extension. */
|
|
108
|
+
export function isSupportedExtension(filePath: string): boolean {
|
|
109
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
110
|
+
return SUPPORTED_EXTENSIONS.has(ext);
|
|
111
|
+
}
|
|
112
|
+
|
|
106
113
|
/**
|
|
107
114
|
* Check whether a POSIX relative path matches any blacklist pattern.
|
|
108
115
|
* Each pattern is tested against every segment (directory or file name) of the path:
|
|
@@ -173,24 +180,27 @@ async function collectAllFiles(
|
|
|
173
180
|
if (isBlacklisted(relPath + "/_", blacklist)) continue;
|
|
174
181
|
await collectAllFiles(path.join(absDir, entry.name), relPath, blacklist, out);
|
|
175
182
|
} else if (entry.isFile()) {
|
|
176
|
-
if (!isBlacklisted(relPath, blacklist)) {
|
|
183
|
+
if (!isBlacklisted(relPath, blacklist) && isSupportedExtension(entry.name)) {
|
|
177
184
|
out.push(relPath);
|
|
178
185
|
}
|
|
179
186
|
}
|
|
180
187
|
}
|
|
181
188
|
}
|
|
182
189
|
|
|
183
|
-
/** Check whether a resolved absolute path is within the
|
|
184
|
-
export function
|
|
190
|
+
/** Check whether a resolved absolute path is within the workspace boundary. */
|
|
191
|
+
export function isWithinWorkspace(resolvedPath: string, workspaceDir: string): boolean {
|
|
185
192
|
const normalizedWorkspace = path.resolve(workspaceDir);
|
|
186
193
|
const normalizedTarget = path.resolve(resolvedPath);
|
|
194
|
+
return normalizedTarget.startsWith(normalizedWorkspace + path.sep) || normalizedTarget === normalizedWorkspace;
|
|
195
|
+
}
|
|
187
196
|
|
|
188
|
-
|
|
189
|
-
|
|
197
|
+
/** Check whether a resolved absolute path is within the allowed memory directories. */
|
|
198
|
+
export function isAllowedMemoryPath(resolvedPath: string, workspaceDir: string): boolean {
|
|
199
|
+
if (!isWithinWorkspace(resolvedPath, workspaceDir)) {
|
|
190
200
|
return false;
|
|
191
201
|
}
|
|
192
202
|
|
|
193
|
-
const relative = path.relative(
|
|
203
|
+
const relative = path.relative(path.resolve(workspaceDir), path.resolve(resolvedPath));
|
|
194
204
|
|
|
195
205
|
// Allow MEMORY.md at root
|
|
196
206
|
if (relative === "MEMORY.md") return true;
|
package/src/service.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { SeahorseClient } from "./client.js";
|
|
|
6
6
|
import type { SeahorseConfig } from "./types.js";
|
|
7
7
|
import { DEFAULT_BLACKLIST } from "./types.js";
|
|
8
8
|
import { SyncState } from "./sync-state.js";
|
|
9
|
-
import { sanitizeError, findMemoryFiles, findWorkspaceFiles, isAllowedMemoryPath, isBlacklisted, toPosixPath } from "./helpers.js";
|
|
9
|
+
import { sanitizeError, findMemoryFiles, findWorkspaceFiles, isAllowedMemoryPath, isBlacklisted, isSupportedExtension, toPosixPath } from "./helpers.js";
|
|
10
10
|
|
|
11
11
|
const DEBOUNCE_MS = 1500;
|
|
12
12
|
|
|
@@ -96,6 +96,7 @@ export function registerSyncService(
|
|
|
96
96
|
const processFileEvent = (relPath: string) => {
|
|
97
97
|
if (config.syncWorkspace) {
|
|
98
98
|
if (isBlacklisted(relPath, mergedBlacklist)) return;
|
|
99
|
+
if (!isSupportedExtension(relPath)) return;
|
|
99
100
|
} else {
|
|
100
101
|
const absCheck = path.resolve(workspaceDir, relPath);
|
|
101
102
|
if (!isAllowedMemoryPath(absCheck, workspaceDir)) return;
|
package/src/tools.ts
CHANGED
|
@@ -5,7 +5,8 @@ import type { SeahorseConfig } from "./types.js";
|
|
|
5
5
|
import type { OpenClawPluginApi, OpenClawPluginToolContext, AgentToolResult } from "openclaw/plugin-sdk";
|
|
6
6
|
import { jsonResult } from "openclaw/plugin-sdk";
|
|
7
7
|
import { SeahorseSearchError, type SeahorseClient } from "./client.js";
|
|
8
|
-
import { sanitizeError, isAllowedMemoryPath, toPosixPath } from "./helpers.js";
|
|
8
|
+
import { sanitizeError, isAllowedMemoryPath, isWithinWorkspace, isBlacklisted, toPosixPath } from "./helpers.js";
|
|
9
|
+
import { DEFAULT_BLACKLIST } from "./types.js";
|
|
9
10
|
|
|
10
11
|
type ToolResult = AgentToolResult;
|
|
11
12
|
|
|
@@ -82,8 +83,9 @@ export function registerMemorySearchTool(
|
|
|
82
83
|
(_ctx: OpenClawPluginToolContext) => ({
|
|
83
84
|
name: "memory_search",
|
|
84
85
|
label: "Memory Search",
|
|
85
|
-
description:
|
|
86
|
-
"Search
|
|
86
|
+
description: config.syncWorkspace
|
|
87
|
+
? "Search across all workspace files using semantic vector search powered by Seahorse."
|
|
88
|
+
: "Search memories stored in MEMORY.md and memory/*.md files using semantic vector search powered by Seahorse.",
|
|
87
89
|
parameters: Type.Object({
|
|
88
90
|
query: Type.String({ description: "Search query" }),
|
|
89
91
|
maxResults: Type.Optional(
|
|
@@ -171,14 +173,15 @@ export function registerMemorySearchTool(
|
|
|
171
173
|
/**
|
|
172
174
|
* Register the `memory_get` tool on the given plugin API.
|
|
173
175
|
*/
|
|
174
|
-
export function registerMemoryGetTool(api: OpenClawPluginApi): void {
|
|
176
|
+
export function registerMemoryGetTool(api: OpenClawPluginApi, config: SeahorseConfig): void {
|
|
175
177
|
api.registerTool(
|
|
176
178
|
(ctx: OpenClawPluginToolContext) => {
|
|
177
179
|
return {
|
|
178
180
|
name: "memory_get",
|
|
179
181
|
label: "Memory Get",
|
|
180
|
-
description:
|
|
181
|
-
"Read a
|
|
182
|
+
description: config.syncWorkspace
|
|
183
|
+
? "Read a workspace file by path. Returns the file content with optional line range."
|
|
184
|
+
: "Read a memory file by path. Returns the file content with optional line range.",
|
|
182
185
|
parameters: Type.Object({
|
|
183
186
|
path: Type.String({ description: "Relative path to memory file" }),
|
|
184
187
|
from: Type.Optional(
|
|
@@ -212,13 +215,33 @@ export function registerMemoryGetTool(api: OpenClawPluginApi): void {
|
|
|
212
215
|
|
|
213
216
|
const resolvedPath = path.resolve(workspaceDir, relativePath);
|
|
214
217
|
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
218
|
+
if (config.syncWorkspace) {
|
|
219
|
+
if (!isWithinWorkspace(resolvedPath, workspaceDir)) {
|
|
220
|
+
api.logger.warn(`[openclaw-seahorse] memory_get: access denied for "${relativePath}"`);
|
|
221
|
+
return jsonResult({
|
|
222
|
+
path: relativePath,
|
|
223
|
+
text: "",
|
|
224
|
+
error: `Access denied. Path "${relativePath}" is outside the workspace.`,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
const mergedBlacklist = [...DEFAULT_BLACKLIST, ...config.syncBlacklist];
|
|
228
|
+
if (isBlacklisted(toPosixPath(relativePath), mergedBlacklist)) {
|
|
229
|
+
api.logger.warn(`[openclaw-seahorse] memory_get: blacklisted path "${relativePath}"`);
|
|
230
|
+
return jsonResult({
|
|
231
|
+
path: relativePath,
|
|
232
|
+
text: "",
|
|
233
|
+
error: `Access denied. Path "${relativePath}" is blacklisted.`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
} else {
|
|
237
|
+
if (!isAllowedMemoryPath(resolvedPath, workspaceDir)) {
|
|
238
|
+
api.logger.warn(`[openclaw-seahorse] memory_get: access denied for "${relativePath}"`);
|
|
239
|
+
return jsonResult({
|
|
240
|
+
path: relativePath,
|
|
241
|
+
text: "",
|
|
242
|
+
error: `Access denied. Path "${relativePath}" is outside the allowed memory directories (MEMORY.md, memory/).`,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
222
245
|
}
|
|
223
246
|
|
|
224
247
|
// Guard against symlink traversal: verify the real path stays within workspace
|
package/src/types.ts
CHANGED
|
@@ -42,6 +42,21 @@ export const DEFAULTS = {
|
|
|
42
42
|
/** Default blacklist patterns applied when syncWorkspace is enabled. */
|
|
43
43
|
export const DEFAULT_BLACKLIST: readonly string[] = [".*"] as const;
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* File extensions supported by Seahorse Storage for processing.
|
|
47
|
+
* Unsupported extensions are filtered out before upload to avoid unnecessary API calls.
|
|
48
|
+
*/
|
|
49
|
+
export const SUPPORTED_EXTENSIONS: ReadonlySet<string> = new Set([
|
|
50
|
+
// 문서 (11)
|
|
51
|
+
".doc", ".docx", ".hwp", ".hwpx", ".odg", ".odp", ".odt", ".pdf", ".ppt", ".pptx", ".rtf",
|
|
52
|
+
// 데이터 (9)
|
|
53
|
+
".csv", ".json", ".ods", ".toml", ".tsv", ".xls", ".xlsx", ".yaml", ".yml",
|
|
54
|
+
// 이미지 (8)
|
|
55
|
+
".bmp", ".gif", ".jpeg", ".jpg", ".png", ".tif", ".tiff", ".webp",
|
|
56
|
+
// 텍스트/마크업 (9)
|
|
57
|
+
".epub", ".htm", ".html", ".markdown", ".md", ".text", ".txt", ".xhtml", ".xml",
|
|
58
|
+
]);
|
|
59
|
+
|
|
45
60
|
/** Seahorse RRF default k parameter (server default). */
|
|
46
61
|
export const RRF_DEFAULT_K = 60;
|
|
47
62
|
|