openclaw-cloudflare-vectorize-memory 0.1.6 → 0.1.8
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 +11 -0
- package/dist/cli.js.map +1 -1
- package/dist/cloudflare-api.js +23 -13
- package/dist/cloudflare-api.js.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/embeddings-client.js +13 -7
- package/dist/embeddings-client.js.map +1 -1
- package/dist/index.js +34 -34
- package/dist/index.js.map +1 -1
- package/dist/record-mapper.js +33 -32
- package/dist/record-mapper.js.map +1 -1
- package/dist/service.js +37 -21
- package/dist/service.js.map +1 -1
- package/dist/types/cloudflare-api.d.ts +3 -0
- package/dist/types/service.d.ts +1 -0
- package/dist/vectorize-client.js +2 -1
- package/dist/vectorize-client.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -63,8 +63,18 @@ npm run publish:npmjs
|
|
|
63
63
|
|
|
64
64
|
The script runs `check`, `test`, and `build` before calling `npm publish --access public`.
|
|
65
65
|
|
|
66
|
+
Before publishing, fill in the placeholder values in `.env`, set `OPENCLAW_CF_MEMORY_RUN_LIVE_INTEGRATION=1`, and run:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm run test:integration
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The live integration suite rebuilds the package first, resolves Cloudflare settings from `.env`, then fills any untouched placeholder values from `.env.example`, and exercises `doctor --create-index` plus a real `upsert` / `search` / `delete` round-trip against the configured backend. The default `npm test` command keeps running only the fast local test suite.
|
|
73
|
+
|
|
66
74
|
## Recommended environment variables
|
|
67
75
|
|
|
76
|
+
The repository includes `.env` and `.env.example` templates for the live integration tests. Replace the placeholder values in `.env` before enabling the suite.
|
|
77
|
+
|
|
68
78
|
Cloudflare-standard variables:
|
|
69
79
|
|
|
70
80
|
```bash
|
|
@@ -86,6 +96,7 @@ Optional:
|
|
|
86
96
|
set CLOUDFLARE_VECTORIZE_NAMESPACE=my-shared-namespace
|
|
87
97
|
set OPENCLAW_CF_MEMORY_STORAGE_MODE=companion-store
|
|
88
98
|
set OPENCLAW_CF_MEMORY_COMPANION_PATH=C:\path\to\companion-store.json
|
|
99
|
+
set OPENCLAW_CF_MEMORY_TEST_NAMESPACE_PREFIX=cf-memory-live
|
|
89
100
|
```
|
|
90
101
|
|
|
91
102
|
If `CLOUDFLARE_VECTORIZE_NAMESPACE` is omitted, the plugin derives namespaces from the active OpenClaw agent/session when possible.
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import type { OpenClawConfig } from \"openclaw/plugin-sdk/config-runtime\";\r\nimport { CLI_ROOT_COMMAND, CLI_ROOT_DESCRIPTION } from \"./constants.js\";\r\nimport type { DoctorReport, IndexInitializationReport, MetadataFilter, MigrationDuplicateStrategy, SmokeTestReport } from \"./types.js\";\r\nimport { formatMigrationSummary, runCloudflareMemoryMigration } from \"./migration.js\";\r\nimport { createCloudflareMemoryService } from \"./service-factory.js\";\r\n\r\ntype CliCommand = {\r\n\tcommand: (name: string) => CliCommand;\r\n\tdescription: (description: string) => CliCommand;\r\n\targument: (name: string, description: string) => CliCommand;\r\n\toption: (flags: string, description: string) => CliCommand;\r\n\taction: (handler: (...args: unknown[]) => Promise<void> | void) => CliCommand;\r\n\topts?: () => Record<string, unknown>;\r\n};\r\n\r\nfunction printJson(value: unknown): void {\r\n\tconsole.log(JSON.stringify(value, null, 2));\r\n}\r\n\r\nfunction printCheckReport(report: DoctorReport | IndexInitializationReport | SmokeTestReport): void {\r\n\tfor (const check of report.checks) {\r\n\t\tconsole.log(`[${check.status}] ${check.name}: ${check.message}`);\r\n\t}\r\n}\r\n\r\nfunction parseMetadataFlag(value: string | undefined): Record<string, string | number | boolean> | undefined {\r\n\tif (!value) {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst parsed = JSON.parse(value) as unknown;\r\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\r\n\t\tthrow new Error(\"--metadata must be a JSON object.\");\r\n\t}\r\n\treturn parsed as Record<string, string | number | boolean>;\r\n}\r\n\r\nfunction parseFilterFlag(value: string | undefined): MetadataFilter | undefined {\r\n\tif (!value) {\r\n\t\treturn undefined;\r\n\t}\r\n\tconst parsed = JSON.parse(value) as unknown;\r\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\r\n\t\tthrow new Error(\"--filter must be a JSON object.\");\r\n\t}\r\n\treturn parsed as MetadataFilter;\r\n}\r\n\r\nfunction isCliCommand(value: unknown): value is CliCommand {\r\n\treturn Boolean(value) && typeof value === \"object\" && typeof (value as CliCommand).opts === \"function\";\r\n}\r\n\r\nfunction resolveInvocation(args: unknown[]): { positionals: unknown[]; options: Record<string, unknown> } {\r\n\tconst maybeCommand = args.at(-1);\r\n\tif (!isCliCommand(maybeCommand)) {\r\n\t\treturn {\r\n\t\t\tpositionals: args,\r\n\t\t\toptions: {},\r\n\t\t};\r\n\t}\r\n\treturn {\r\n\t\tpositionals: args.slice(0, -1),\r\n\t\toptions: maybeCommand.opts?.() ?? {},\r\n\t};\r\n}\r\n\r\nfunction parseDuplicateStrategy(value: unknown): MigrationDuplicateStrategy | undefined {\r\n\tif (value === undefined) {\r\n\t\treturn undefined;\r\n\t}\r\n\tif (value === \"overwrite\" || value === \"skip\" || value === \"fail\") {\r\n\t\treturn value;\r\n\t}\r\n\tthrow new Error(\"--if-exists must be overwrite, skip, or fail.\");\r\n}\r\n\r\nexport function registerCloudflareMemoryCli(\r\n\tprogram: {\r\n\t\tcommand: (name: string) => CliCommand;\r\n\t},\r\n\tparams: {\r\n\t\tpluginConfig: unknown;\r\n\t\topenClawConfig: OpenClawConfig;\r\n\t\tresolvePath?: (input: string) => string;\r\n\t},\r\n): void {\r\n\tconst root = program.command(CLI_ROOT_COMMAND).description(CLI_ROOT_DESCRIPTION);\r\n\r\n\tfunction resolveOptions(args: unknown[]): Record<string, unknown> {\r\n\t\treturn resolveInvocation(args).options;\r\n\t}\r\n\r\n\troot\r\n\t\t.command(\"doctor\")\r\n\t\t.description(\"Validate Workers AI and Vectorize configuration.\")\r\n\t\t.option(\"--create-index\", \"Create the Vectorize index if missing.\")\r\n\t\t.option(\"--json\", \"Print structured JSON output.\")\r\n\t\t.action(async (...args) => {\r\n\t\t\tconst options = resolveOptions(args);\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst report = await service.doctor({\r\n\t\t\t\tcreateIndexIfMissing: Boolean(options.createIndex),\r\n\t\t\t});\r\n\t\t\tif (options.json) {\r\n\t\t\t\tprintJson(report);\r\n\t\t\t} else {\r\n\t\t\t\tprintCheckReport(report);\r\n\t\t\t}\r\n\t\t\tif (!report.ok) {\r\n\t\t\t\tprocess.exitCode = 1;\r\n\t\t\t}\r\n\t\t});\r\n\r\n\troot\r\n\t\t.command(\"init\")\r\n\t\t.description(\"Initialize the Cloudflare Vectorize index for the configured embedding model.\")\r\n\t\t.option(\"--json\", \"Print structured JSON output.\")\r\n\t\t.action(async (...args) => {\r\n\t\t\tconst options = resolveOptions(args);\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst report = await service.initializeIndex();\r\n\t\t\tif (options.json) {\r\n\t\t\t\tprintJson(report);\r\n\t\t\t} else {\r\n\t\t\t\tprintCheckReport(report);\r\n\t\t\t}\r\n\t\t\tif (!report.ok) {\r\n\t\t\t\tprocess.exitCode = 1;\r\n\t\t\t}\r\n\t\t});\r\n\r\n\troot\r\n\t\t.command(\"test\")\r\n\t\t.description(\"Run an end-to-end embedding and semantic-search smoke test.\")\r\n\t\t.option(\"--json\", \"Print structured JSON output.\")\r\n\t\t.action(async (...args) => {\r\n\t\t\tconst options = resolveOptions(args);\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst report = await service.runSmokeTest();\r\n\t\t\tif (options.json) {\r\n\t\t\t\tprintJson(report);\r\n\t\t\t} else {\r\n\t\t\t\tprintCheckReport(report);\r\n\t\t\t}\r\n\t\t\tif (!report.ok) {\r\n\t\t\t\tprocess.exitCode = 1;\r\n\t\t\t}\r\n\t\t});\r\n\r\n\troot\r\n\t\t.command(\"search\")\r\n\t\t.description(\"Search stored Cloudflare memory.\")\r\n\t\t.argument(\"<query>\", \"Semantic search query.\")\r\n\t\t.option(\"--namespace <namespace>\", \"Optional namespace override.\")\r\n\t\t.option(\"--limit <count>\", \"Maximum number of results.\")\r\n\t\t.option(\"--filter <json>\", \"Optional metadata filter JSON.\")\r\n\t\t.action(async (query, opts) => {\r\n\t\t\tconst options = opts as Record<string, unknown>;\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst results = await service.search({\r\n\t\t\t\tquery: String(query),\r\n\t\t\t\tnamespace: options.namespace as string | undefined,\r\n\t\t\t\tmaxResults: options.limit ? Number(options.limit) : undefined,\r\n\t\t\t\tfilter: parseFilterFlag(options.filter as string | undefined),\r\n\t\t\t});\r\n\t\t\tprintJson(results);\r\n\t\t});\r\n\r\n\troot\r\n\t\t.command(\"upsert\")\r\n\t\t.description(\"Insert or update a memory record.\")\r\n\t\t.argument(\"<text>\", \"Memory text.\")\r\n\t\t.option(\"--id <id>\", \"Stable logical id.\")\r\n\t\t.option(\"--title <title>\", \"Optional title.\")\r\n\t\t.option(\"--namespace <namespace>\", \"Optional namespace override.\")\r\n\t\t.option(\"--source <source>\", \"Optional source label.\")\r\n\t\t.option(\"--metadata <json>\", \"Optional metadata JSON object.\")\r\n\t\t.action(async (text, opts) => {\r\n\t\t\tconst options = opts as Record<string, unknown>;\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst result = await service.upsert({\r\n\t\t\t\tinput: {\r\n\t\t\t\t\tid: options.id as string | undefined,\r\n\t\t\t\t\ttitle: options.title as string | undefined,\r\n\t\t\t\t\ttext: String(text),\r\n\t\t\t\t\tnamespace: options.namespace as string | undefined,\r\n\t\t\t\t\tsource: options.source as string | undefined,\r\n\t\t\t\t\tmetadata: parseMetadataFlag(options.metadata as string | undefined),\r\n\t\t\t\t},\r\n\t\t\t});\r\n\t\t\tprintJson(result);\r\n\t\t});\r\n\r\n\troot\r\n\t\t.command(\"delete\")\r\n\t\t.description(\"Delete a memory record.\")\r\n\t\t.argument(\"<id>\", \"Logical memory record id.\")\r\n\t\t.option(\"--namespace <namespace>\", \"Optional namespace override.\")\r\n\t\t.action(async (id, opts) => {\r\n\t\t\tconst options = opts as Record<string, unknown>;\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst mutationId = await service.delete({\r\n\t\t\t\tid: String(id),\r\n\t\t\t\tnamespace: options.namespace as string | undefined,\r\n\t\t\t});\r\n\t\t\tprintJson({ id, mutationId });\r\n\t\t});\r\n\r\n\troot\r\n\t\t.command(\"migrate\")\r\n\t\t.description(\"Migrate legacy markdown memory into Cloudflare Vectorize.\")\r\n\t\t.argument(\"[sources...]\", \"Markdown files, directories, or glob patterns. Defaults to the current OpenClaw memory corpus when omitted.\")\r\n\t\t.option(\"--workspace <path>\", \"Workspace root used for default-provider discovery and relative path normalization.\")\r\n\t\t.option(\"--namespace <namespace>\", \"Target namespace override.\")\r\n\t\t.option(\"--derive-namespace-from-path\", \"Derive namespaces from the first relative path segment instead of using a single target namespace.\")\r\n\t\t.option(\"--if-exists <strategy>\", \"Duplicate handling: overwrite, skip, or fail.\")\r\n\t\t.option(\"--create-index\", \"Create the Vectorize index if missing.\")\r\n\t\t.option(\"--dry-run\", \"Plan the migration without writing records.\")\r\n\t\t.option(\"--json\", \"Print structured JSON output.\")\r\n\t\t.action(async (...args) => {\r\n\t\t\tconst { positionals, options } = resolveInvocation(args);\r\n\t\t\tconst rawSources = positionals[0];\r\n\t\t\tconst sourcePaths =\r\n\t\t\t\tpositionals.length === 0 ? [] : Array.isArray(rawSources) ? rawSources.map((value) => String(value)) : positionals.map((value) => String(value));\r\n\t\t\tconst service = await createCloudflareMemoryService({\r\n\t\t\t\tpluginConfig: params.pluginConfig,\r\n\t\t\t\topenClawConfig: params.openClawConfig,\r\n\t\t\t\tenv: process.env,\r\n\t\t\t\tresolvePath: params.resolvePath,\r\n\t\t\t});\r\n\t\t\tconst summary = await runCloudflareMemoryMigration({\r\n\t\t\t\tservice,\r\n\t\t\t\toptions: {\r\n\t\t\t\t\tsourcePaths,\r\n\t\t\t\t\tworkspaceDir: options.workspace as string | undefined,\r\n\t\t\t\t\tnamespace: options.namespace as string | undefined,\r\n\t\t\t\t\tnamespaceStrategy: options.deriveNamespaceFromPath ? \"path\" : \"single-target\",\r\n\t\t\t\t\tduplicateStrategy: parseDuplicateStrategy(options.ifExists),\r\n\t\t\t\t\tdryRun: Boolean(options.dryRun),\r\n\t\t\t\t\tcreateIndexIfMissing: Boolean(options.createIndex),\r\n\t\t\t\t},\r\n\t\t\t});\r\n\t\t\tif (options.json) {\r\n\t\t\t\tprintJson(summary);\r\n\t\t\t} else {\r\n\t\t\t\tconsole.log(formatMigrationSummary(summary));\r\n\t\t\t}\r\n\t\t\tif (summary.failed > 0) {\r\n\t\t\t\tprocess.exitCode = 1;\r\n\t\t\t}\r\n\t\t});\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import type { OpenClawConfig } from \"openclaw/plugin-sdk/config-runtime\";\nimport { CLI_ROOT_COMMAND, CLI_ROOT_DESCRIPTION } from \"./constants.js\";\nimport type { DoctorReport, IndexInitializationReport, MetadataFilter, MigrationDuplicateStrategy, SmokeTestReport } from \"./types.js\";\nimport { formatMigrationSummary, runCloudflareMemoryMigration } from \"./migration.js\";\nimport { createCloudflareMemoryService } from \"./service-factory.js\";\n\ntype CliCommand = {\n\tcommand: (name: string) => CliCommand;\n\tdescription: (description: string) => CliCommand;\n\targument: (name: string, description: string) => CliCommand;\n\toption: (flags: string, description: string) => CliCommand;\n\taction: (handler: (...args: unknown[]) => Promise<void> | void) => CliCommand;\n\topts?: () => Record<string, unknown>;\n};\n\nfunction printJson(value: unknown): void {\n\tconsole.log(JSON.stringify(value, null, 2));\n}\n\nfunction printCheckReport(report: DoctorReport | IndexInitializationReport | SmokeTestReport): void {\n\tfor (const check of report.checks) {\n\t\tconsole.log(`[${check.status}] ${check.name}: ${check.message}`);\n\t}\n}\n\nfunction parseMetadataFlag(value: string | undefined): Record<string, string | number | boolean> | undefined {\n\tif (!value) {\n\t\treturn undefined;\n\t}\n\tconst parsed = JSON.parse(value) as unknown;\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(\"--metadata must be a JSON object.\");\n\t}\n\treturn parsed as Record<string, string | number | boolean>;\n}\n\nfunction parseFilterFlag(value: string | undefined): MetadataFilter | undefined {\n\tif (!value) {\n\t\treturn undefined;\n\t}\n\tconst parsed = JSON.parse(value) as unknown;\n\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(\"--filter must be a JSON object.\");\n\t}\n\treturn parsed as MetadataFilter;\n}\n\nfunction isCliCommand(value: unknown): value is CliCommand {\n\treturn Boolean(value) && typeof value === \"object\" && typeof (value as CliCommand).opts === \"function\";\n}\n\nfunction resolveInvocation(args: unknown[]): { positionals: unknown[]; options: Record<string, unknown> } {\n\tconst maybeCommand = args.at(-1);\n\tif (!isCliCommand(maybeCommand)) {\n\t\treturn {\n\t\t\tpositionals: args,\n\t\t\toptions: {},\n\t\t};\n\t}\n\treturn {\n\t\tpositionals: args.slice(0, -1),\n\t\toptions: maybeCommand.opts?.() ?? {},\n\t};\n}\n\nfunction parseDuplicateStrategy(value: unknown): MigrationDuplicateStrategy | undefined {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\tif (value === \"overwrite\" || value === \"skip\" || value === \"fail\") {\n\t\treturn value;\n\t}\n\tthrow new Error(\"--if-exists must be overwrite, skip, or fail.\");\n}\n\nexport function registerCloudflareMemoryCli(\n\tprogram: {\n\t\tcommand: (name: string) => CliCommand;\n\t},\n\tparams: {\n\t\tpluginConfig: unknown;\n\t\topenClawConfig: OpenClawConfig;\n\t\tresolvePath?: (input: string) => string;\n\t},\n): void {\n\tconst root = program.command(CLI_ROOT_COMMAND).description(CLI_ROOT_DESCRIPTION);\n\n\tfunction resolveOptions(args: unknown[]): Record<string, unknown> {\n\t\treturn resolveInvocation(args).options;\n\t}\n\n\troot\n\t\t.command(\"doctor\")\n\t\t.description(\"Validate Workers AI and Vectorize configuration.\")\n\t\t.option(\"--create-index\", \"Create the Vectorize index if missing.\")\n\t\t.option(\"--json\", \"Print structured JSON output.\")\n\t\t.action(async (...args) => {\n\t\t\tconst options = resolveOptions(args);\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst report = await service.doctor({\n\t\t\t\tcreateIndexIfMissing: Boolean(options.createIndex),\n\t\t\t});\n\t\t\tif (options.json) {\n\t\t\t\tprintJson(report);\n\t\t\t} else {\n\t\t\t\tprintCheckReport(report);\n\t\t\t}\n\t\t\tif (!report.ok) {\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t}\n\t\t});\n\n\troot\n\t\t.command(\"init\")\n\t\t.description(\"Initialize the Cloudflare Vectorize index for the configured embedding model.\")\n\t\t.option(\"--json\", \"Print structured JSON output.\")\n\t\t.action(async (...args) => {\n\t\t\tconst options = resolveOptions(args);\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst report = await service.initializeIndex();\n\t\t\tif (options.json) {\n\t\t\t\tprintJson(report);\n\t\t\t} else {\n\t\t\t\tprintCheckReport(report);\n\t\t\t}\n\t\t\tif (!report.ok) {\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t}\n\t\t});\n\n\troot\n\t\t.command(\"test\")\n\t\t.description(\"Run an end-to-end embedding and semantic-search smoke test.\")\n\t\t.option(\"--json\", \"Print structured JSON output.\")\n\t\t.action(async (...args) => {\n\t\t\tconst options = resolveOptions(args);\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst report = await service.runSmokeTest();\n\t\t\tif (options.json) {\n\t\t\t\tprintJson(report);\n\t\t\t} else {\n\t\t\t\tprintCheckReport(report);\n\t\t\t}\n\t\t\tif (!report.ok) {\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t}\n\t\t});\n\n\troot\n\t\t.command(\"search\")\n\t\t.description(\"Search stored Cloudflare memory.\")\n\t\t.argument(\"<query>\", \"Semantic search query.\")\n\t\t.option(\"--namespace <namespace>\", \"Optional namespace override.\")\n\t\t.option(\"--limit <count>\", \"Maximum number of results.\")\n\t\t.option(\"--filter <json>\", \"Optional metadata filter JSON.\")\n\t\t.action(async (query, opts) => {\n\t\t\tconst options = opts as Record<string, unknown>;\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst results = await service.search({\n\t\t\t\tquery: String(query),\n\t\t\t\tnamespace: options.namespace as string | undefined,\n\t\t\t\tmaxResults: options.limit ? Number(options.limit) : undefined,\n\t\t\t\tfilter: parseFilterFlag(options.filter as string | undefined),\n\t\t\t});\n\t\t\tprintJson(results);\n\t\t});\n\n\troot\n\t\t.command(\"upsert\")\n\t\t.description(\"Insert or update a memory record.\")\n\t\t.argument(\"<text>\", \"Memory text.\")\n\t\t.option(\"--id <id>\", \"Stable logical id.\")\n\t\t.option(\"--title <title>\", \"Optional title.\")\n\t\t.option(\"--namespace <namespace>\", \"Optional namespace override.\")\n\t\t.option(\"--source <source>\", \"Optional source label.\")\n\t\t.option(\"--metadata <json>\", \"Optional metadata JSON object.\")\n\t\t.action(async (text, opts) => {\n\t\t\tconst options = opts as Record<string, unknown>;\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst result = await service.upsert({\n\t\t\t\tinput: {\n\t\t\t\t\tid: options.id as string | undefined,\n\t\t\t\t\ttitle: options.title as string | undefined,\n\t\t\t\t\ttext: String(text),\n\t\t\t\t\tnamespace: options.namespace as string | undefined,\n\t\t\t\t\tsource: options.source as string | undefined,\n\t\t\t\t\tmetadata: parseMetadataFlag(options.metadata as string | undefined),\n\t\t\t\t},\n\t\t\t});\n\t\t\tprintJson(result);\n\t\t});\n\n\troot\n\t\t.command(\"delete\")\n\t\t.description(\"Delete a memory record.\")\n\t\t.argument(\"<id>\", \"Logical memory record id.\")\n\t\t.option(\"--namespace <namespace>\", \"Optional namespace override.\")\n\t\t.action(async (id, opts) => {\n\t\t\tconst options = opts as Record<string, unknown>;\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst mutationId = await service.delete({\n\t\t\t\tid: String(id),\n\t\t\t\tnamespace: options.namespace as string | undefined,\n\t\t\t});\n\t\t\tprintJson({ id, mutationId });\n\t\t});\n\n\troot\n\t\t.command(\"migrate\")\n\t\t.description(\"Migrate legacy markdown memory into Cloudflare Vectorize.\")\n\t\t.argument(\"[sources...]\", \"Markdown files, directories, or glob patterns. Defaults to the current OpenClaw memory corpus when omitted.\")\n\t\t.option(\"--workspace <path>\", \"Workspace root used for default-provider discovery and relative path normalization.\")\n\t\t.option(\"--namespace <namespace>\", \"Target namespace override.\")\n\t\t.option(\"--derive-namespace-from-path\", \"Derive namespaces from the first relative path segment instead of using a single target namespace.\")\n\t\t.option(\"--if-exists <strategy>\", \"Duplicate handling: overwrite, skip, or fail.\")\n\t\t.option(\"--create-index\", \"Create the Vectorize index if missing.\")\n\t\t.option(\"--dry-run\", \"Plan the migration without writing records.\")\n\t\t.option(\"--json\", \"Print structured JSON output.\")\n\t\t.action(async (...args) => {\n\t\t\tconst { positionals, options } = resolveInvocation(args);\n\t\t\tconst rawSources = positionals[0];\n\t\t\tconst sourcePaths =\n\t\t\t\tpositionals.length === 0 ? [] : Array.isArray(rawSources) ? rawSources.map((value) => String(value)) : positionals.map((value) => String(value));\n\t\t\tconst service = await createCloudflareMemoryService({\n\t\t\t\tpluginConfig: params.pluginConfig,\n\t\t\t\topenClawConfig: params.openClawConfig,\n\t\t\t\tenv: process.env,\n\t\t\t\tresolvePath: params.resolvePath,\n\t\t\t});\n\t\t\tconst summary = await runCloudflareMemoryMigration({\n\t\t\t\tservice,\n\t\t\t\toptions: {\n\t\t\t\t\tsourcePaths,\n\t\t\t\t\tworkspaceDir: options.workspace as string | undefined,\n\t\t\t\t\tnamespace: options.namespace as string | undefined,\n\t\t\t\t\tnamespaceStrategy: options.deriveNamespaceFromPath ? \"path\" : \"single-target\",\n\t\t\t\t\tduplicateStrategy: parseDuplicateStrategy(options.ifExists),\n\t\t\t\t\tdryRun: Boolean(options.dryRun),\n\t\t\t\t\tcreateIndexIfMissing: Boolean(options.createIndex),\n\t\t\t\t},\n\t\t\t});\n\t\t\tif (options.json) {\n\t\t\t\tprintJson(summary);\n\t\t\t} else {\n\t\t\t\tconsole.log(formatMigrationSummary(summary));\n\t\t\t}\n\t\t\tif (summary.failed > 0) {\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t}\n\t\t});\n}\n"],"mappings":"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"}
|
package/dist/cloudflare-api.js
CHANGED
|
@@ -3,22 +3,32 @@ import { CloudflareApiError as e } from "./errors.js";
|
|
|
3
3
|
function t(e, t) {
|
|
4
4
|
return e?.errors?.map((e) => e.message).filter(Boolean).join("; ") || t;
|
|
5
5
|
}
|
|
6
|
-
function n(
|
|
6
|
+
function n(e) {
|
|
7
|
+
return typeof e == "object" && !!e && "success" in e;
|
|
8
|
+
}
|
|
9
|
+
function r(e) {
|
|
10
|
+
if (e) return JSON.parse(e);
|
|
11
|
+
}
|
|
12
|
+
function i(t) {
|
|
7
13
|
return t instanceof e && t.status === 404;
|
|
8
14
|
}
|
|
9
|
-
async function
|
|
10
|
-
let
|
|
11
|
-
|
|
12
|
-
let
|
|
13
|
-
method:
|
|
14
|
-
headers:
|
|
15
|
-
body:
|
|
16
|
-
}),
|
|
17
|
-
if (!
|
|
18
|
-
if (
|
|
19
|
-
|
|
15
|
+
async function a(i) {
|
|
16
|
+
let a = new Headers(i.headers);
|
|
17
|
+
a.set("Authorization", `Bearer ${i.apiToken}`), !a.has("Content-Type") && i.body && a.set("Content-Type", "application/json");
|
|
18
|
+
let o = await fetch(i.url, {
|
|
19
|
+
method: i.method ?? (i.body ? "POST" : "GET"),
|
|
20
|
+
headers: a,
|
|
21
|
+
body: i.body
|
|
22
|
+
}), s = r(await o.text());
|
|
23
|
+
if (!o.ok) throw new e(t(n(s) ? s : void 0, `Cloudflare request failed with ${o.status}.`), o.status, s);
|
|
24
|
+
if (n(s)) {
|
|
25
|
+
if (!s.success) throw new e(t(s, "Cloudflare request failed."), o.status, s);
|
|
26
|
+
return s.result;
|
|
27
|
+
}
|
|
28
|
+
if ((i.responseMode ?? "envelope") === "auto") return s;
|
|
29
|
+
throw new e("Cloudflare response did not include the expected success/result envelope.", o.status, s);
|
|
20
30
|
}
|
|
21
31
|
//#endregion
|
|
22
|
-
export {
|
|
32
|
+
export { i as isCloudflareNotFoundError, a as requestCloudflare };
|
|
23
33
|
|
|
24
34
|
//# sourceMappingURL=cloudflare-api.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloudflare-api.js","names":[],"sources":["../src/cloudflare-api.ts"],"sourcesContent":["import { CloudflareApiError } from \"./errors.js\";\n\ntype CloudflareResponseEnvelope<T> = {\n\tsuccess: boolean;\n\tresult: T;\n\terrors?: Array<{ code?: number; message?: string }>;\n};\n\nfunction describeFailure(envelope: Partial<CloudflareResponseEnvelope<unknown>> | undefined, fallback: string): string {\n\tconst message = envelope?.errors\n\t\t?.map((entry) => entry.message)\n\t\t.filter(Boolean)\n\t\t.join(\"; \");\n\treturn message || fallback;\n}\n\nexport function isCloudflareNotFoundError(error: unknown): boolean {\n\treturn error instanceof CloudflareApiError && error.status === 404;\n}\n\nexport async function requestCloudflare<T>(params: {\n\turl: string;\n\tapiToken: string;\n\tmethod?: string;\n\theaders?: Record<string, string>;\n\tbody?: string;\n}): Promise<T> {\n\tconst headers = new Headers(params.headers);\n\theaders.set(\"Authorization\", `Bearer ${params.apiToken}`);\n\tif (!headers.has(\"Content-Type\") && params.body) {\n\t\theaders.set(\"Content-Type\", \"application/json\");\n\t}\n\n\tconst response = await fetch(params.url, {\n\t\tmethod: params.method ?? (params.body ? \"POST\" : \"GET\"),\n\t\theaders,\n\t\tbody: params.body,\n\t});\n\n\tconst rawText = await response.text();\n\tconst parsed =
|
|
1
|
+
{"version":3,"file":"cloudflare-api.js","names":[],"sources":["../src/cloudflare-api.ts"],"sourcesContent":["import { CloudflareApiError } from \"./errors.js\";\r\n\r\ntype CloudflareResponseEnvelope<T> = {\r\n\tsuccess: boolean;\r\n\tresult: T;\r\n\terrors?: Array<{ code?: number; message?: string }>;\r\n};\r\n\r\ntype ResponseMode = \"auto\" | \"envelope\";\r\n\r\nfunction describeFailure(envelope: Partial<CloudflareResponseEnvelope<unknown>> | undefined, fallback: string): string {\r\n\tconst message = envelope?.errors\r\n\t\t?.map((entry) => entry.message)\r\n\t\t.filter(Boolean)\r\n\t\t.join(\"; \");\r\n\treturn message || fallback;\r\n}\r\n\r\nfunction isResponseEnvelope<T>(value: unknown): value is Partial<CloudflareResponseEnvelope<T>> & { success: boolean } {\r\n\treturn value !== null && typeof value === \"object\" && \"success\" in value;\r\n}\r\n\r\nfunction parseResponseBody(rawText: string): unknown {\r\n\tif (!rawText) {\r\n\t\treturn undefined;\r\n\t}\r\n\treturn JSON.parse(rawText) as unknown;\r\n}\r\n\r\nexport function isCloudflareNotFoundError(error: unknown): boolean {\r\n\treturn error instanceof CloudflareApiError && error.status === 404;\r\n}\r\n\r\nexport async function requestCloudflare<T>(params: {\r\n\turl: string;\r\n\tapiToken: string;\r\n\tmethod?: string;\r\n\theaders?: Record<string, string>;\r\n\tbody?: string;\r\n\tresponseMode?: ResponseMode;\r\n}): Promise<T> {\r\n\tconst headers = new Headers(params.headers);\r\n\theaders.set(\"Authorization\", `Bearer ${params.apiToken}`);\r\n\tif (!headers.has(\"Content-Type\") && params.body) {\r\n\t\theaders.set(\"Content-Type\", \"application/json\");\r\n\t}\r\n\r\n\tconst response = await fetch(params.url, {\r\n\t\tmethod: params.method ?? (params.body ? \"POST\" : \"GET\"),\r\n\t\theaders,\r\n\t\tbody: params.body,\r\n\t});\r\n\r\n\tconst rawText = await response.text();\r\n\tconst parsed = parseResponseBody(rawText);\r\n\tif (!response.ok) {\r\n\t\tthrow new CloudflareApiError(\r\n\t\t\tdescribeFailure(isResponseEnvelope(parsed) ? parsed : undefined, `Cloudflare request failed with ${response.status}.`),\r\n\t\t\tresponse.status,\r\n\t\t\tparsed,\r\n\t\t);\r\n\t}\r\n\r\n\tif (isResponseEnvelope<T>(parsed)) {\r\n\t\tif (!parsed.success) {\r\n\t\t\tthrow new CloudflareApiError(describeFailure(parsed, \"Cloudflare request failed.\"), response.status, parsed);\r\n\t\t}\r\n\t\treturn parsed.result as T;\r\n\t}\r\n\r\n\tif ((params.responseMode ?? \"envelope\") === \"auto\") {\r\n\t\treturn parsed as T;\r\n\t}\r\n\r\n\tthrow new CloudflareApiError(\"Cloudflare response did not include the expected success/result envelope.\", response.status, parsed);\r\n}\r\n"],"mappings":";;AAUA,SAAS,EAAgB,GAAoE,GAA0B;AAKtH,QAJgB,GAAU,QACvB,KAAK,MAAU,EAAM,QAAQ,CAC9B,OAAO,QAAQ,CACf,KAAK,KAAK,IACM;;AAGnB,SAAS,EAAsB,GAAwF;AACtH,QAAyB,OAAO,KAAU,cAAnC,KAA+C,aAAa;;AAGpE,SAAS,EAAkB,GAA0B;AAC/C,OAGL,QAAO,KAAK,MAAM,EAAQ;;AAG3B,SAAgB,EAA0B,GAAyB;AAClE,QAAO,aAAiB,KAAsB,EAAM,WAAW;;AAGhE,eAAsB,EAAqB,GAO5B;CACd,IAAM,IAAU,IAAI,QAAQ,EAAO,QAAQ;AAE3C,CADA,EAAQ,IAAI,iBAAiB,UAAU,EAAO,WAAW,EACrD,CAAC,EAAQ,IAAI,eAAe,IAAI,EAAO,QAC1C,EAAQ,IAAI,gBAAgB,mBAAmB;CAGhD,IAAM,IAAW,MAAM,MAAM,EAAO,KAAK;EACxC,QAAQ,EAAO,WAAW,EAAO,OAAO,SAAS;EACjD;EACA,MAAM,EAAO;EACb,CAAC,EAGI,IAAS,EADC,MAAM,EAAS,MAAM,CACI;AACzC,KAAI,CAAC,EAAS,GACb,OAAM,IAAI,EACT,EAAgB,EAAmB,EAAO,GAAG,IAAS,KAAA,GAAW,kCAAkC,EAAS,OAAO,GAAG,EACtH,EAAS,QACT,EACA;AAGF,KAAI,EAAsB,EAAO,EAAE;AAClC,MAAI,CAAC,EAAO,QACX,OAAM,IAAI,EAAmB,EAAgB,GAAQ,6BAA6B,EAAE,EAAS,QAAQ,EAAO;AAE7G,SAAO,EAAO;;AAGf,MAAK,EAAO,gBAAgB,gBAAgB,OAC3C,QAAO;AAGR,OAAM,IAAI,EAAmB,6EAA6E,EAAS,QAAQ,EAAO"}
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["import type { StorageMode, VectorizeMetric } from \"./types.js\";\
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../src/constants.ts"],"sourcesContent":["import type { StorageMode, VectorizeMetric } from \"./types.js\";\n\nexport const PLUGIN_ID = \"memory-cloudflare-vectorize\";\nexport const PLUGIN_NAME = \"Cloudflare Vectorize Memory\";\nexport const PLUGIN_DESCRIPTION = \"OpenClaw memory plugin backed by Cloudflare Vectorize and Workers AI embeddings.\";\nexport const CLI_ROOT_COMMAND = \"cf-memory\";\nexport const CLI_ROOT_DESCRIPTION = \"Manage Cloudflare memory records.\";\nexport const CLI_ROOT_DESCRIPTOR = {\n\tname: CLI_ROOT_COMMAND,\n\tdescription: CLI_ROOT_DESCRIPTION,\n\thasSubcommands: true,\n} as const;\n\nexport const CLOUDFLARE_ACCOUNT_ID_ENV = \"CLOUDFLARE_ACCOUNT_ID\";\nexport const CLOUDFLARE_API_TOKEN_ENV = \"CLOUDFLARE_API_TOKEN\";\nexport const VECTORIZE_INDEX_ENV = \"CLOUDFLARE_VECTORIZE_INDEX_NAME\";\nexport const VECTORIZE_NAMESPACE_ENV = \"CLOUDFLARE_VECTORIZE_NAMESPACE\";\nexport const WORKERS_AI_MODEL_ENV = \"CLOUDFLARE_WORKERS_AI_EMBEDDING_MODEL\";\nexport const VECTORIZE_TOP_K_ENV = \"CLOUDFLARE_VECTORIZE_TOP_K\";\nexport const STORAGE_MODE_ENV = \"OPENCLAW_CF_MEMORY_STORAGE_MODE\";\nexport const COMPANION_PATH_ENV = \"OPENCLAW_CF_MEMORY_COMPANION_PATH\";\n\nexport const DEFAULT_EMBEDDING_MODEL = \"@cf/baai/bge-base-en-v1.5\";\nexport const DEFAULT_TOP_K = 5;\nexport const DEFAULT_MIN_SCORE = 0;\nexport const DEFAULT_INLINE_TEXT_MAX_BYTES = 6_000;\nexport const DEFAULT_VECTORIZE_METRIC: VectorizeMetric = \"cosine\";\nexport const DEFAULT_STORAGE_MODE: StorageMode = \"vectorize-inline\";\nexport const DEFAULT_CLOUDFLARE_API_BASE_URL = \"https://api.cloudflare.com/client/v4\";\nexport const DEFAULT_INDEX_DESCRIPTION = \"OpenClaw memory index backed by Cloudflare Vectorize.\";\n\nexport const RESERVED_METADATA_PREFIX = \"oc_\";\nexport const RESERVED_METADATA_KEYS = {\n\tlogicalId: \"oc_record_id\",\n\ttitle: \"oc_title\",\n\ttext: \"oc_text\",\n\tstorageMode: \"oc_storage_mode\",\n\tpointer: \"oc_pointer\",\n\tsource: \"oc_source\",\n\tcreatedAt: \"oc_created_at\",\n\tupdatedAt: \"oc_updated_at\",\n} as const;\n"],"mappings":";AAEA,IAAa,IAAY,+BACZ,IAAc,+BACd,IAAqB,oFACrB,IAAmB,aACnB,IAAuB,qCACvB,IAAsB;CAClC,MAAM;CACN,aAAa;CACb,gBAAgB;CAChB,EAEY,IAA4B,yBAC5B,IAA2B,wBAC3B,IAAsB,mCACtB,IAA0B,kCAC1B,IAAuB,yCACvB,IAAsB,8BACtB,IAAmB,mCACnB,IAAqB,qCAErB,IAA0B,6BAG1B,IAAgC,KAChC,IAA4C,UAC5C,IAAoC,oBACpC,IAAkC,wCAClC,IAA4B,yDAG5B,IAAyB;CACrC,WAAW;CACX,OAAO;CACP,MAAM;CACN,aAAa;CACb,SAAS;CACT,QAAQ;CACR,WAAW;CACX,WAAW;CACX"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { requestCloudflare as e } from "./cloudflare-api.js";
|
|
2
2
|
//#region src/embeddings-client.ts
|
|
3
|
-
|
|
3
|
+
function t(e) {
|
|
4
|
+
return e.data.every((e) => !!e && typeof e == "object" && "embedding" in e && Array.isArray(e.embedding) && "index" in e && typeof e.index == "number");
|
|
5
|
+
}
|
|
6
|
+
var n = class {
|
|
4
7
|
constructor(e) {
|
|
5
8
|
this.config = e;
|
|
6
9
|
}
|
|
@@ -8,21 +11,24 @@ var t = class {
|
|
|
8
11
|
let [t] = await this.embedBatch([e]);
|
|
9
12
|
return t;
|
|
10
13
|
}
|
|
11
|
-
async embedBatch(
|
|
12
|
-
|
|
14
|
+
async embedBatch(n) {
|
|
15
|
+
if (n.length === 0) return [];
|
|
16
|
+
let r = await e({
|
|
13
17
|
url: `${this.config.workersAiBaseUrl}/embeddings`,
|
|
14
18
|
apiToken: this.config.apiToken,
|
|
15
19
|
body: JSON.stringify({
|
|
16
20
|
model: this.config.model,
|
|
17
|
-
input:
|
|
18
|
-
})
|
|
19
|
-
|
|
21
|
+
input: n
|
|
22
|
+
}),
|
|
23
|
+
responseMode: "auto"
|
|
24
|
+
});
|
|
25
|
+
return t(r) ? [...r.data].sort((e, t) => e.index - t.index).map((e) => e.embedding) : r.data;
|
|
20
26
|
}
|
|
21
27
|
async probeDimensions() {
|
|
22
28
|
return (await this.embedQuery("openclaw-memory-dimension-probe")).length;
|
|
23
29
|
}
|
|
24
30
|
};
|
|
25
31
|
//#endregion
|
|
26
|
-
export {
|
|
32
|
+
export { n as WorkersAiEmbeddingsClient };
|
|
27
33
|
|
|
28
34
|
//# sourceMappingURL=embeddings-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings-client.js","names":[],"sources":["../src/embeddings-client.ts"],"sourcesContent":["import { requestCloudflare } from \"./cloudflare-api.js\";\nimport type { ResolvedPluginConfig } from \"./types.js\";\n\ntype OpenAiEmbeddingResponse = {\n\tdata: Array<{\n\t\tembedding: number[];\n\t\tindex: number;\n\t}>;\n};\n\nexport class WorkersAiEmbeddingsClient {\n\tconstructor(private readonly config: ResolvedPluginConfig) {}\n\n\tasync embedQuery(text: string): Promise<number[]> {\n\t\tconst [embedding] = await this.embedBatch([text]);\n\t\treturn embedding;\n\t}\n\n\tasync embedBatch(texts: string[]): Promise<number[][]> {\n\t\tif (texts.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\tconst response = await requestCloudflare<
|
|
1
|
+
{"version":3,"file":"embeddings-client.js","names":[],"sources":["../src/embeddings-client.ts"],"sourcesContent":["import { requestCloudflare } from \"./cloudflare-api.js\";\r\nimport type { ResolvedPluginConfig } from \"./types.js\";\r\n\r\ntype OpenAiEmbeddingResponse = {\r\n\tdata: Array<{\r\n\t\tembedding: number[];\r\n\t\tindex: number;\r\n\t}>;\r\n};\r\n\r\ntype NativeEmbeddingResponse = {\r\n\tdata: number[][];\r\n\tshape?: number[];\r\n};\r\n\r\ntype EmbeddingResponse = OpenAiEmbeddingResponse | NativeEmbeddingResponse;\r\n\r\nfunction isOpenAiEmbeddingResponse(response: EmbeddingResponse): response is OpenAiEmbeddingResponse {\r\n\treturn response.data.every(\r\n\t\t(entry) =>\r\n\t\t\tBoolean(entry) &&\r\n\t\t\ttypeof entry === \"object\" &&\r\n\t\t\t\"embedding\" in entry &&\r\n\t\t\tArray.isArray(entry.embedding) &&\r\n\t\t\t\"index\" in entry &&\r\n\t\t\ttypeof entry.index === \"number\",\r\n\t);\r\n}\r\n\r\nexport class WorkersAiEmbeddingsClient {\r\n\tconstructor(private readonly config: ResolvedPluginConfig) {}\r\n\r\n\tasync embedQuery(text: string): Promise<number[]> {\r\n\t\tconst [embedding] = await this.embedBatch([text]);\r\n\t\treturn embedding;\r\n\t}\r\n\r\n\tasync embedBatch(texts: string[]): Promise<number[][]> {\r\n\t\tif (texts.length === 0) {\r\n\t\t\treturn [];\r\n\t\t}\r\n\t\tconst response = await requestCloudflare<EmbeddingResponse>({\r\n\t\t\turl: `${this.config.workersAiBaseUrl}/embeddings`,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tmodel: this.config.model,\r\n\t\t\t\tinput: texts,\r\n\t\t\t}),\r\n\t\t\tresponseMode: \"auto\",\r\n\t\t});\r\n\r\n\t\tif (isOpenAiEmbeddingResponse(response)) {\r\n\t\t\treturn [...response.data].sort((left, right) => left.index - right.index).map((entry) => entry.embedding);\r\n\t\t}\r\n\r\n\t\treturn response.data;\r\n\t}\r\n\r\n\tasync probeDimensions(): Promise<number> {\r\n\t\tconst embedding = await this.embedQuery(\"openclaw-memory-dimension-probe\");\r\n\t\treturn embedding.length;\r\n\t}\r\n}\r\n"],"mappings":";;AAiBA,SAAS,EAA0B,GAAkE;AACpG,QAAO,EAAS,KAAK,OACnB,MACA,EAAQ,KACR,OAAO,KAAU,YACjB,eAAe,KACf,MAAM,QAAQ,EAAM,UAAU,IAC9B,WAAW,KACX,OAAO,EAAM,SAAU,SACxB;;AAGF,IAAa,IAAb,MAAuC;CACtC,YAAY,GAA+C;AAA9B,OAAA,SAAA;;CAE7B,MAAM,WAAW,GAAiC;EACjD,IAAM,CAAC,KAAa,MAAM,KAAK,WAAW,CAAC,EAAK,CAAC;AACjD,SAAO;;CAGR,MAAM,WAAW,GAAsC;AACtD,MAAI,EAAM,WAAW,EACpB,QAAO,EAAE;EAEV,IAAM,IAAW,MAAM,EAAqC;GAC3D,KAAK,GAAG,KAAK,OAAO,iBAAiB;GACrC,UAAU,KAAK,OAAO;GACtB,MAAM,KAAK,UAAU;IACpB,OAAO,KAAK,OAAO;IACnB,OAAO;IACP,CAAC;GACF,cAAc;GACd,CAAC;AAMF,SAJI,EAA0B,EAAS,GAC/B,CAAC,GAAG,EAAS,KAAK,CAAC,MAAM,GAAM,MAAU,EAAK,QAAQ,EAAM,MAAM,CAAC,KAAK,MAAU,EAAM,UAAU,GAGnG,EAAS;;CAGjB,MAAM,kBAAmC;AAExC,UADkB,MAAM,KAAK,WAAW,kCAAkC,EACzD"}
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getPluginConfigFromOpenClawConfig as
|
|
3
|
-
import { CloudflareMemoryService as
|
|
4
|
-
import { registerCloudflareMemoryCli as
|
|
5
|
-
import { buildPromptSection as
|
|
6
|
-
import { createPublicArtifactsProvider as
|
|
7
|
-
import { createMemoryRuntime as
|
|
8
|
-
import { createDeleteTool as
|
|
9
|
-
import { definePluginEntry as
|
|
1
|
+
import { CLI_ROOT_COMMAND as e, CLI_ROOT_DESCRIPTOR as t, DEFAULT_EMBEDDING_MODEL as n, PLUGIN_DESCRIPTION as r, PLUGIN_ID as i, PLUGIN_NAME as a } from "./constants.js";
|
|
2
|
+
import { getPluginConfigFromOpenClawConfig as o, pluginConfigSchema as s, resolvePluginConfig as c } from "./config.js";
|
|
3
|
+
import { CloudflareMemoryService as l } from "./service.js";
|
|
4
|
+
import { registerCloudflareMemoryCli as u } from "./cli.js";
|
|
5
|
+
import { buildPromptSection as d } from "./prompt.js";
|
|
6
|
+
import { createPublicArtifactsProvider as f } from "./public-artifacts.js";
|
|
7
|
+
import { createMemoryRuntime as p } from "./runtime.js";
|
|
8
|
+
import { createDeleteTool as m, createGetTool as h, createSearchTool as g, createUpsertTool as _ } from "./tools.js";
|
|
9
|
+
import { definePluginEntry as v } from "openclaw/plugin-sdk/plugin-entry";
|
|
10
10
|
//#region src/index.ts
|
|
11
|
-
function
|
|
11
|
+
function y() {
|
|
12
12
|
return {
|
|
13
13
|
id: "cloudflare-workers-ai",
|
|
14
|
-
defaultModel:
|
|
14
|
+
defaultModel: n,
|
|
15
15
|
transport: "remote",
|
|
16
16
|
allowExplicitWhenConfiguredAuto: !0,
|
|
17
17
|
async create(e) {
|
|
18
|
-
let t = await
|
|
19
|
-
pluginConfig:
|
|
18
|
+
let t = await c({
|
|
19
|
+
pluginConfig: o(e.config),
|
|
20
20
|
openClawConfig: e.config,
|
|
21
21
|
env: process.env
|
|
22
|
-
}), n = new
|
|
22
|
+
}), n = new l({
|
|
23
23
|
...t,
|
|
24
24
|
model: e.model || t.model,
|
|
25
25
|
workersAiBaseUrl: e.remote?.baseUrl && e.remote.baseUrl.trim().length > 0 ? e.remote.baseUrl : t.workersAiBaseUrl,
|
|
@@ -43,43 +43,43 @@ function v() {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
pluginConfig:
|
|
50
|
-
openClawConfig:
|
|
51
|
-
resolvePath:
|
|
46
|
+
function b(n) {
|
|
47
|
+
n.registerCli(({ program: e }) => {
|
|
48
|
+
u(e, {
|
|
49
|
+
pluginConfig: n.pluginConfig,
|
|
50
|
+
openClawConfig: n.config,
|
|
51
|
+
resolvePath: n.resolvePath
|
|
52
52
|
});
|
|
53
|
-
}, { descriptors: [e] });
|
|
53
|
+
}, n.registrationMode === "cli-metadata" ? { descriptors: [t] } : { commands: [e] });
|
|
54
54
|
}
|
|
55
|
-
var
|
|
56
|
-
id:
|
|
57
|
-
name:
|
|
58
|
-
description:
|
|
55
|
+
var x = v({
|
|
56
|
+
id: i,
|
|
57
|
+
name: a,
|
|
58
|
+
description: r,
|
|
59
59
|
kind: "memory",
|
|
60
|
-
configSchema:
|
|
60
|
+
configSchema: s,
|
|
61
61
|
register(e) {
|
|
62
|
-
|
|
63
|
-
promptBuilder:
|
|
64
|
-
runtime:
|
|
62
|
+
s.parse?.(e.pluginConfig ?? {}), b(e), !(e.registrationMode === "cli-metadata" || typeof e.registerMemoryEmbeddingProvider != "function" || typeof e.registerMemoryCapability != "function" || typeof e.registerTool != "function") && (e.registerMemoryEmbeddingProvider(y()), e.registerMemoryCapability({
|
|
63
|
+
promptBuilder: d,
|
|
64
|
+
runtime: p({
|
|
65
65
|
pluginConfig: e.pluginConfig,
|
|
66
66
|
resolvePath: e.resolvePath
|
|
67
67
|
}),
|
|
68
|
-
publicArtifacts:
|
|
69
|
-
}), e.registerTool((t) =>
|
|
68
|
+
publicArtifacts: f(e.pluginConfig, e.resolvePath)
|
|
69
|
+
}), e.registerTool((t) => g(e.pluginConfig, t), { names: ["cloudflare_memory_search"] }), e.registerTool((t) => h(e.pluginConfig, t), { names: ["cloudflare_memory_get"] }), e.registerTool((t) => _(e.pluginConfig, t), { names: ["cloudflare_memory_upsert"] }), e.registerTool((t) => m(e.pluginConfig, t), { names: ["cloudflare_memory_delete"] }), c({
|
|
70
70
|
pluginConfig: e.pluginConfig,
|
|
71
71
|
openClawConfig: e.config,
|
|
72
72
|
env: process.env,
|
|
73
73
|
resolvePath: e.resolvePath
|
|
74
74
|
}).then((t) => {
|
|
75
|
-
e.logger.info(`${
|
|
75
|
+
e.logger.info(`${i}: registered for index ${t.indexName} using model ${t.model}.`);
|
|
76
76
|
}).catch((t) => {
|
|
77
77
|
let n = t instanceof Error ? t.message : "Unknown configuration error.";
|
|
78
|
-
e.logger.warn(`${
|
|
78
|
+
e.logger.warn(`${i}: deferred config validation reported: ${n}`);
|
|
79
79
|
}));
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
82
|
//#endregion
|
|
83
|
-
export {
|
|
83
|
+
export { x as default };
|
|
84
84
|
|
|
85
85
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { MemoryEmbeddingProviderAdapter } from \"openclaw/plugin-sdk/memory-core-host-engine-embeddings\";\
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { MemoryEmbeddingProviderAdapter } from \"openclaw/plugin-sdk/memory-core-host-engine-embeddings\";\nimport { definePluginEntry, type OpenClawPluginApi } from \"openclaw/plugin-sdk/plugin-entry\";\nimport { registerCloudflareMemoryCli } from \"./cli.js\";\nimport { getPluginConfigFromOpenClawConfig, pluginConfigSchema, resolvePluginConfig } from \"./config.js\";\nimport { CLI_ROOT_COMMAND, CLI_ROOT_DESCRIPTOR, DEFAULT_EMBEDDING_MODEL, PLUGIN_DESCRIPTION, PLUGIN_ID, PLUGIN_NAME } from \"./constants.js\";\nimport { buildPromptSection } from \"./prompt.js\";\nimport { createPublicArtifactsProvider } from \"./public-artifacts.js\";\nimport { createMemoryRuntime } from \"./runtime.js\";\nimport { CloudflareMemoryService } from \"./service.js\";\nimport { createDeleteTool, createGetTool, createSearchTool, createUpsertTool } from \"./tools.js\";\n\nfunction createMemoryEmbeddingProviderAdapter(): MemoryEmbeddingProviderAdapter {\n\treturn {\n\t\tid: \"cloudflare-workers-ai\",\n\t\tdefaultModel: DEFAULT_EMBEDDING_MODEL,\n\t\ttransport: \"remote\",\n\t\tallowExplicitWhenConfiguredAuto: true,\n\t\tasync create(options) {\n\t\t\tconst pluginConfig = getPluginConfigFromOpenClawConfig(options.config);\n\t\t\tconst resolved = await resolvePluginConfig({\n\t\t\t\tpluginConfig,\n\t\t\t\topenClawConfig: options.config,\n\t\t\t\tenv: process.env,\n\t\t\t});\n\t\t\tconst service = new CloudflareMemoryService(\n\t\t\t\t{\n\t\t\t\t\t...resolved,\n\t\t\t\t\tmodel: options.model || resolved.model,\n\t\t\t\t\tworkersAiBaseUrl: options.remote?.baseUrl && options.remote.baseUrl.trim().length > 0 ? options.remote.baseUrl : resolved.workersAiBaseUrl,\n\t\t\t\t\tapiToken: typeof options.remote?.apiKey === \"string\" && options.remote.apiKey.trim().length > 0 ? options.remote.apiKey : resolved.apiToken,\n\t\t\t\t},\n\t\t\t\toptions.config,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tprovider: {\n\t\t\t\t\tid: \"cloudflare-workers-ai\",\n\t\t\t\t\tmodel: options.model || resolved.model,\n\t\t\t\t\tembedQuery: (text) => service.embeddings.embedQuery(text),\n\t\t\t\t\tembedBatch: (texts) => service.embeddings.embedBatch(texts),\n\t\t\t\t},\n\t\t\t\truntime: {\n\t\t\t\t\tid: \"cloudflare-workers-ai\",\n\t\t\t\t\tcacheKeyData: {\n\t\t\t\t\t\taccountId: resolved.accountId,\n\t\t\t\t\t\tmodel: options.model || resolved.model,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n}\n\nfunction registerCloudflareMemoryCliEntry(api: Pick<OpenClawPluginApi, \"registerCli\" | \"pluginConfig\" | \"config\" | \"resolvePath\" | \"registrationMode\">): void {\n\tapi.registerCli(\n\t\t({ program }) => {\n\t\t\tregisterCloudflareMemoryCli(program, {\n\t\t\t\tpluginConfig: api.pluginConfig,\n\t\t\t\topenClawConfig: api.config,\n\t\t\t\tresolvePath: api.resolvePath,\n\t\t\t});\n\t\t},\n\t\tapi.registrationMode === \"cli-metadata\"\n\t\t\t? {\n\t\t\t\t\tdescriptors: [CLI_ROOT_DESCRIPTOR],\n\t\t\t\t}\n\t\t\t: {\n\t\t\t\t\tcommands: [CLI_ROOT_COMMAND],\n\t\t\t\t},\n\t);\n}\n\nexport default definePluginEntry({\n\tid: PLUGIN_ID,\n\tname: PLUGIN_NAME,\n\tdescription: PLUGIN_DESCRIPTION,\n\tkind: \"memory\",\n\tconfigSchema: pluginConfigSchema,\n\tregister(api: OpenClawPluginApi) {\n\t\tpluginConfigSchema.parse?.(api.pluginConfig ?? {});\n\n\t\tregisterCloudflareMemoryCliEntry(api);\n\n\t\tif (\n\t\t\tapi.registrationMode === \"cli-metadata\" ||\n\t\t\ttypeof api.registerMemoryEmbeddingProvider !== \"function\" ||\n\t\t\ttypeof api.registerMemoryCapability !== \"function\" ||\n\t\t\ttypeof api.registerTool !== \"function\"\n\t\t) {\n\t\t\treturn;\n\t\t}\n\n\t\tapi.registerMemoryEmbeddingProvider(createMemoryEmbeddingProviderAdapter());\n\t\tapi.registerMemoryCapability({\n\t\t\tpromptBuilder: buildPromptSection,\n\t\t\truntime: createMemoryRuntime({\n\t\t\t\tpluginConfig: api.pluginConfig,\n\t\t\t\tresolvePath: api.resolvePath,\n\t\t\t}),\n\t\t\tpublicArtifacts: createPublicArtifactsProvider(api.pluginConfig, api.resolvePath),\n\t\t});\n\n\t\tapi.registerTool((ctx) => createSearchTool(api.pluginConfig, ctx), {\n\t\t\tnames: [\"cloudflare_memory_search\"],\n\t\t});\n\t\tapi.registerTool((ctx) => createGetTool(api.pluginConfig, ctx), {\n\t\t\tnames: [\"cloudflare_memory_get\"],\n\t\t});\n\t\tapi.registerTool((ctx) => createUpsertTool(api.pluginConfig, ctx), {\n\t\t\tnames: [\"cloudflare_memory_upsert\"],\n\t\t});\n\t\tapi.registerTool((ctx) => createDeleteTool(api.pluginConfig, ctx), {\n\t\t\tnames: [\"cloudflare_memory_delete\"],\n\t\t});\n\n\t\tvoid resolvePluginConfig({\n\t\t\tpluginConfig: api.pluginConfig,\n\t\t\topenClawConfig: api.config,\n\t\t\tenv: process.env,\n\t\t\tresolvePath: api.resolvePath,\n\t\t})\n\t\t\t.then((resolved) => {\n\t\t\t\tapi.logger.info(`${PLUGIN_ID}: registered for index ${resolved.indexName} using model ${resolved.model}.`);\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"Unknown configuration error.\";\n\t\t\t\tapi.logger.warn(`${PLUGIN_ID}: deferred config validation reported: ${message}`);\n\t\t\t});\n\t},\n});\n"],"mappings":";;;;;;;;;;AAWA,SAAS,IAAuE;AAC/E,QAAO;EACN,IAAI;EACJ,cAAc;EACd,WAAW;EACX,iCAAiC;EACjC,MAAM,OAAO,GAAS;GAErB,IAAM,IAAW,MAAM,EAAoB;IAC1C,cAFoB,EAAkC,EAAQ,OAAO;IAGrE,gBAAgB,EAAQ;IACxB,KAAK,QAAQ;IACb,CAAC,EACI,IAAU,IAAI,EACnB;IACC,GAAG;IACH,OAAO,EAAQ,SAAS,EAAS;IACjC,kBAAkB,EAAQ,QAAQ,WAAW,EAAQ,OAAO,QAAQ,MAAM,CAAC,SAAS,IAAI,EAAQ,OAAO,UAAU,EAAS;IAC1H,UAAU,OAAO,EAAQ,QAAQ,UAAW,YAAY,EAAQ,OAAO,OAAO,MAAM,CAAC,SAAS,IAAI,EAAQ,OAAO,SAAS,EAAS;IACnI,EACD,EAAQ,OACR;AACD,UAAO;IACN,UAAU;KACT,IAAI;KACJ,OAAO,EAAQ,SAAS,EAAS;KACjC,aAAa,MAAS,EAAQ,WAAW,WAAW,EAAK;KACzD,aAAa,MAAU,EAAQ,WAAW,WAAW,EAAM;KAC3D;IACD,SAAS;KACR,IAAI;KACJ,cAAc;MACb,WAAW,EAAS;MACpB,OAAO,EAAQ,SAAS,EAAS;MACjC;KACD;IACD;;EAEF;;AAGF,SAAS,EAAiC,GAAoH;AAC7J,GAAI,aACF,EAAE,iBAAc;AAChB,IAA4B,GAAS;GACpC,cAAc,EAAI;GAClB,gBAAgB,EAAI;GACpB,aAAa,EAAI;GACjB,CAAC;IAEH,EAAI,qBAAqB,iBACtB,EACA,aAAa,CAAC,EAAoB,EAClC,GACA,EACA,UAAU,CAAC,EAAiB,EAC5B,CACH;;AAGF,IAAA,IAAe,EAAkB;CAChC,IAAI;CACJ,MAAM;CACN,aAAa;CACb,MAAM;CACN,cAAc;CACd,SAAS,GAAwB;AAChC,IAAmB,QAAQ,EAAI,gBAAgB,EAAE,CAAC,EAElD,EAAiC,EAAI,EAGpC,IAAI,qBAAqB,kBACzB,OAAO,EAAI,mCAAoC,cAC/C,OAAO,EAAI,4BAA6B,cACxC,OAAO,EAAI,gBAAiB,gBAK7B,EAAI,gCAAgC,GAAsC,CAAC,EAC3E,EAAI,yBAAyB;GAC5B,eAAe;GACf,SAAS,EAAoB;IAC5B,cAAc,EAAI;IAClB,aAAa,EAAI;IACjB,CAAC;GACF,iBAAiB,EAA8B,EAAI,cAAc,EAAI,YAAY;GACjF,CAAC,EAEF,EAAI,cAAc,MAAQ,EAAiB,EAAI,cAAc,EAAI,EAAE,EAClE,OAAO,CAAC,2BAA2B,EACnC,CAAC,EACF,EAAI,cAAc,MAAQ,EAAc,EAAI,cAAc,EAAI,EAAE,EAC/D,OAAO,CAAC,wBAAwB,EAChC,CAAC,EACF,EAAI,cAAc,MAAQ,EAAiB,EAAI,cAAc,EAAI,EAAE,EAClE,OAAO,CAAC,2BAA2B,EACnC,CAAC,EACF,EAAI,cAAc,MAAQ,EAAiB,EAAI,cAAc,EAAI,EAAE,EAClE,OAAO,CAAC,2BAA2B,EACnC,CAAC,EAEG,EAAoB;GACxB,cAAc,EAAI;GAClB,gBAAgB,EAAI;GACpB,KAAK,QAAQ;GACb,aAAa,EAAI;GACjB,CAAC,CACA,MAAM,MAAa;AACnB,KAAI,OAAO,KAAK,GAAG,EAAU,yBAAyB,EAAS,UAAU,eAAe,EAAS,MAAM,GAAG;IACzG,CACD,OAAO,MAAmB;GAC1B,IAAM,IAAU,aAAiB,QAAQ,EAAM,UAAU;AACzD,KAAI,OAAO,KAAK,GAAG,EAAU,yCAAyC,IAAU;IAC/E;;CAEJ,CAAC"}
|
package/dist/record-mapper.js
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
1
|
import { RESERVED_METADATA_KEYS as e } from "./constants.js";
|
|
2
2
|
import { RecordSizeError as t } from "./errors.js";
|
|
3
|
-
import {
|
|
4
|
-
import { Buffer as
|
|
3
|
+
import { createHash as n, randomUUID as r } from "node:crypto";
|
|
4
|
+
import { Buffer as i } from "node:buffer";
|
|
5
5
|
//#region src/record-mapper.ts
|
|
6
|
-
function
|
|
6
|
+
function a(e) {
|
|
7
7
|
let t = e.trim().replace(/[.$"]/g, "_").replace(/\s+/g, "_");
|
|
8
8
|
return t ? t.startsWith("oc_") ? `user_${t}` : t : "metadata";
|
|
9
9
|
}
|
|
10
|
-
function
|
|
10
|
+
function o(e) {
|
|
11
11
|
if (!e) return {};
|
|
12
12
|
let t = Object.entries(e).filter(([, e]) => [
|
|
13
13
|
"string",
|
|
14
14
|
"number",
|
|
15
15
|
"boolean"
|
|
16
16
|
].includes(typeof e));
|
|
17
|
-
return Object.fromEntries(t.map(([e, t]) => [
|
|
18
|
-
}
|
|
19
|
-
function o(e, t) {
|
|
20
|
-
return `${e}::${t}`;
|
|
17
|
+
return Object.fromEntries(t.map(([e, t]) => [a(e), t]));
|
|
21
18
|
}
|
|
22
19
|
function s(e, t) {
|
|
20
|
+
let r = `${e}::${t}`;
|
|
21
|
+
return i.byteLength(r, "utf8") <= 64 ? r : `cfm_${n("sha256").update(r).digest("hex").slice(0, 48)}`;
|
|
22
|
+
}
|
|
23
|
+
function c(e, t) {
|
|
23
24
|
return `${e}/${t}.md`;
|
|
24
25
|
}
|
|
25
|
-
function
|
|
26
|
+
function l(e) {
|
|
26
27
|
let t = e.replace(/\\/g, "/").replace(/^\//, "").split("/");
|
|
27
28
|
return t.length !== 2 || !t[1].endsWith(".md") ? null : {
|
|
28
29
|
namespace: t[0],
|
|
29
30
|
logicalId: t[1].slice(0, -3)
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
+
function u(e, t) {
|
|
33
34
|
let n = e.trim().replace(/\s+/g, " ");
|
|
34
35
|
if (!n) return "";
|
|
35
36
|
let r = n.toLowerCase().indexOf(t.trim().toLowerCase());
|
|
@@ -37,47 +38,47 @@ function l(e, t) {
|
|
|
37
38
|
let i = Math.max(0, r - 80), a = Math.min(n.length, r + Math.max(t.length, 40) + 80);
|
|
38
39
|
return n.slice(i, a);
|
|
39
40
|
}
|
|
40
|
-
function
|
|
41
|
-
let
|
|
41
|
+
function d(n) {
|
|
42
|
+
let a = n.input.id?.trim() || r(), l = s(n.namespace, a), u = (/* @__PURE__ */ new Date()).toISOString(), d = o(n.input.metadata), f = {
|
|
42
43
|
...d,
|
|
43
|
-
[e.logicalId]:
|
|
44
|
-
[e.storageMode]:
|
|
44
|
+
[e.logicalId]: a,
|
|
45
|
+
[e.storageMode]: n.config.storageMode,
|
|
45
46
|
[e.createdAt]: u,
|
|
46
47
|
[e.updatedAt]: u
|
|
47
48
|
};
|
|
48
|
-
|
|
49
|
+
n.input.title && (f[e.title] = n.input.title), n.input.source && (f[e.source] = n.input.source);
|
|
49
50
|
let p;
|
|
50
|
-
if (
|
|
51
|
-
let
|
|
52
|
-
if (
|
|
53
|
-
f[e.text] =
|
|
51
|
+
if (n.config.storageMode === "vectorize-inline") {
|
|
52
|
+
let r = i.byteLength(n.input.text, "utf8");
|
|
53
|
+
if (r > n.config.inlineTextMaxBytes) throw new t(`Memory text is ${r} bytes, which exceeds the inline metadata limit of ${n.config.inlineTextMaxBytes}. Switch storage.mode to "companion-store" or reduce the payload size.`);
|
|
54
|
+
f[e.text] = n.input.text;
|
|
54
55
|
} else {
|
|
55
|
-
let t =
|
|
56
|
+
let t = c(n.namespace, a);
|
|
56
57
|
f[e.pointer] = t, p = {
|
|
57
|
-
id:
|
|
58
|
-
namespace:
|
|
59
|
-
title:
|
|
60
|
-
text:
|
|
58
|
+
id: a,
|
|
59
|
+
namespace: n.namespace,
|
|
60
|
+
title: n.input.title,
|
|
61
|
+
text: n.input.text,
|
|
61
62
|
metadata: d,
|
|
62
|
-
source:
|
|
63
|
+
source: n.input.source,
|
|
63
64
|
createdAt: u,
|
|
64
65
|
updatedAt: u
|
|
65
66
|
};
|
|
66
67
|
}
|
|
67
68
|
return {
|
|
68
|
-
logicalId:
|
|
69
|
+
logicalId: a,
|
|
69
70
|
vectorId: l,
|
|
70
|
-
path:
|
|
71
|
+
path: c(n.namespace, a),
|
|
71
72
|
vector: {
|
|
72
73
|
id: l,
|
|
73
|
-
namespace:
|
|
74
|
-
values:
|
|
74
|
+
namespace: n.namespace,
|
|
75
|
+
values: n.embedding,
|
|
75
76
|
metadata: f
|
|
76
77
|
},
|
|
77
78
|
companionRecord: p
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
|
-
function
|
|
81
|
+
function f(t) {
|
|
81
82
|
let n = t.metadata ?? {}, r = t.namespace ?? (typeof n[e.pointer] == "string" ? String(n[e.pointer]).split("/")[0] : "main"), i = typeof n[e.logicalId] == "string" ? String(n[e.logicalId]) : String(t.id ?? ""), a = Object.fromEntries(Object.entries(n).filter(([e]) => !e.startsWith("oc_")));
|
|
82
83
|
return {
|
|
83
84
|
logicalId: i,
|
|
@@ -89,10 +90,10 @@ function d(t) {
|
|
|
89
90
|
source: typeof n[e.source] == "string" ? String(n[e.source]) : void 0,
|
|
90
91
|
createdAt: typeof n[e.createdAt] == "string" ? String(n[e.createdAt]) : void 0,
|
|
91
92
|
updatedAt: typeof n[e.updatedAt] == "string" ? String(n[e.updatedAt]) : void 0,
|
|
92
|
-
path:
|
|
93
|
+
path: c(r, i)
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
//#endregion
|
|
96
|
-
export {
|
|
97
|
+
export { u as buildSnippet, s as buildVectorId, f as hydrateInlineRecord, d as mapRecordForUpsert, l as parseVirtualPath };
|
|
97
98
|
|
|
98
99
|
//# sourceMappingURL=record-mapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-mapper.js","names":[],"sources":["../src/record-mapper.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\nimport { randomUUID } from \"node:crypto\";\nimport { RESERVED_METADATA_KEYS, RESERVED_METADATA_PREFIX } from \"./constants.js\";\nimport { RecordSizeError } from \"./errors.js\";\nimport type {\n\tCompanionRecord,\n\tHydratedMemoryRecord,\n\tMemoryRecordInput,\n\tMetadataValue,\n\tResolvedPluginConfig,\n\tVectorizeQueryMatch,\n\tVectorizeVector,\n} from \"./types.js\";\n\nfunction sanitizeMetadataKey(key: string): string {\n\tconst sanitized = key.trim().replace(/[.$\"]/g, \"_\").replace(/\\s+/g, \"_\");\n\tif (!sanitized) {\n\t\treturn \"metadata\";\n\t}\n\tif (sanitized.startsWith(RESERVED_METADATA_PREFIX)) {\n\t\treturn `user_${sanitized}`;\n\t}\n\treturn sanitized;\n}\n\nfunction sanitizeMetadata(metadata: Record<string, MetadataValue> | undefined): Record<string, MetadataValue> {\n\tif (!metadata) {\n\t\treturn {};\n\t}\n\tconst entries = Object.entries(metadata).filter(([, value]) => [\"string\", \"number\", \"boolean\"].includes(typeof value));\n\treturn Object.fromEntries(entries.map(([key, value]) => [sanitizeMetadataKey(key), value]));\n}\n\nexport function buildVectorId(namespace: string, logicalId: string): string {\n\
|
|
1
|
+
{"version":3,"file":"record-mapper.js","names":[],"sources":["../src/record-mapper.ts"],"sourcesContent":["import { Buffer } from \"node:buffer\";\r\nimport { createHash, randomUUID } from \"node:crypto\";\r\nimport { RESERVED_METADATA_KEYS, RESERVED_METADATA_PREFIX } from \"./constants.js\";\r\nimport { RecordSizeError } from \"./errors.js\";\r\nimport type {\r\n\tCompanionRecord,\r\n\tHydratedMemoryRecord,\r\n\tMemoryRecordInput,\r\n\tMetadataValue,\r\n\tResolvedPluginConfig,\r\n\tVectorizeQueryMatch,\r\n\tVectorizeVector,\r\n} from \"./types.js\";\r\n\r\nfunction sanitizeMetadataKey(key: string): string {\r\n\tconst sanitized = key.trim().replace(/[.$\"]/g, \"_\").replace(/\\s+/g, \"_\");\r\n\tif (!sanitized) {\r\n\t\treturn \"metadata\";\r\n\t}\r\n\tif (sanitized.startsWith(RESERVED_METADATA_PREFIX)) {\r\n\t\treturn `user_${sanitized}`;\r\n\t}\r\n\treturn sanitized;\r\n}\r\n\r\nfunction sanitizeMetadata(metadata: Record<string, MetadataValue> | undefined): Record<string, MetadataValue> {\r\n\tif (!metadata) {\r\n\t\treturn {};\r\n\t}\r\n\tconst entries = Object.entries(metadata).filter(([, value]) => [\"string\", \"number\", \"boolean\"].includes(typeof value));\r\n\treturn Object.fromEntries(entries.map(([key, value]) => [sanitizeMetadataKey(key), value]));\r\n}\r\n\r\nexport function buildVectorId(namespace: string, logicalId: string): string {\r\n\tconst rawId = `${namespace}::${logicalId}`;\r\n\tif (Buffer.byteLength(rawId, \"utf8\") <= 64) {\r\n\t\treturn rawId;\r\n\t}\r\n\treturn `cfm_${createHash(\"sha256\").update(rawId).digest(\"hex\").slice(0, 48)}`;\r\n}\r\n\r\nexport function buildVirtualPath(namespace: string, logicalId: string): string {\r\n\treturn `${namespace}/${logicalId}.md`;\r\n}\r\n\r\nexport function parseVirtualPath(path: string): { namespace: string; logicalId: string } | null {\r\n\tconst normalized = path.replace(/\\\\/g, \"/\").replace(/^\\//, \"\");\r\n\tconst segments = normalized.split(\"/\");\r\n\tif (segments.length !== 2 || !segments[1].endsWith(\".md\")) {\r\n\t\treturn null;\r\n\t}\r\n\treturn {\r\n\t\tnamespace: segments[0],\r\n\t\tlogicalId: segments[1].slice(0, -3),\r\n\t};\r\n}\r\n\r\nexport function buildSnippet(text: string, query: string): string {\r\n\tconst normalized = text.trim().replace(/\\s+/g, \" \");\r\n\tif (!normalized) {\r\n\t\treturn \"\";\r\n\t}\r\n\tconst lower = normalized.toLowerCase();\r\n\tconst index = lower.indexOf(query.trim().toLowerCase());\r\n\tif (index === -1) {\r\n\t\treturn normalized.slice(0, 220);\r\n\t}\r\n\tconst start = Math.max(0, index - 80);\r\n\tconst end = Math.min(normalized.length, index + Math.max(query.length, 40) + 80);\r\n\treturn normalized.slice(start, end);\r\n}\r\n\r\nexport function mapRecordForUpsert(params: { input: MemoryRecordInput; namespace: string; embedding: number[]; config: ResolvedPluginConfig }): {\r\n\tlogicalId: string;\r\n\tvectorId: string;\r\n\tpath: string;\r\n\tvector: VectorizeVector;\r\n\tcompanionRecord?: CompanionRecord;\r\n} {\r\n\tconst logicalId = params.input.id?.trim() || randomUUID();\r\n\tconst vectorId = buildVectorId(params.namespace, logicalId);\r\n\tconst now = new Date().toISOString();\r\n\tconst userMetadata = sanitizeMetadata(params.input.metadata);\r\n\tconst metadataBase: Record<string, MetadataValue> = {\r\n\t\t...userMetadata,\r\n\t\t[RESERVED_METADATA_KEYS.logicalId]: logicalId,\r\n\t\t[RESERVED_METADATA_KEYS.storageMode]: params.config.storageMode,\r\n\t\t[RESERVED_METADATA_KEYS.createdAt]: now,\r\n\t\t[RESERVED_METADATA_KEYS.updatedAt]: now,\r\n\t};\r\n\r\n\tif (params.input.title) {\r\n\t\tmetadataBase[RESERVED_METADATA_KEYS.title] = params.input.title;\r\n\t}\r\n\tif (params.input.source) {\r\n\t\tmetadataBase[RESERVED_METADATA_KEYS.source] = params.input.source;\r\n\t}\r\n\r\n\tlet companionRecord: CompanionRecord | undefined;\r\n\tif (params.config.storageMode === \"vectorize-inline\") {\r\n\t\tconst byteLength = Buffer.byteLength(params.input.text, \"utf8\");\r\n\t\tif (byteLength > params.config.inlineTextMaxBytes) {\r\n\t\t\tthrow new RecordSizeError(\r\n\t\t\t\t`Memory text is ${byteLength} bytes, which exceeds the inline metadata limit of ${params.config.inlineTextMaxBytes}. Switch storage.mode to \"companion-store\" or reduce the payload size.`,\r\n\t\t\t);\r\n\t\t}\r\n\t\tmetadataBase[RESERVED_METADATA_KEYS.text] = params.input.text;\r\n\t} else {\r\n\t\tconst pointer = buildVirtualPath(params.namespace, logicalId);\r\n\t\tmetadataBase[RESERVED_METADATA_KEYS.pointer] = pointer;\r\n\t\tcompanionRecord = {\r\n\t\t\tid: logicalId,\r\n\t\t\tnamespace: params.namespace,\r\n\t\t\ttitle: params.input.title,\r\n\t\t\ttext: params.input.text,\r\n\t\t\tmetadata: userMetadata,\r\n\t\t\tsource: params.input.source,\r\n\t\t\tcreatedAt: now,\r\n\t\t\tupdatedAt: now,\r\n\t\t};\r\n\t}\r\n\r\n\treturn {\r\n\t\tlogicalId,\r\n\t\tvectorId,\r\n\t\tpath: buildVirtualPath(params.namespace, logicalId),\r\n\t\tvector: {\r\n\t\t\tid: vectorId,\r\n\t\t\tnamespace: params.namespace,\r\n\t\t\tvalues: params.embedding,\r\n\t\t\tmetadata: metadataBase,\r\n\t\t},\r\n\t\tcompanionRecord,\r\n\t};\r\n}\r\n\r\nexport function hydrateInlineRecord(match: VectorizeQueryMatch): Omit<HydratedMemoryRecord, \"text\" | \"path\"> & {\r\n\ttext?: string;\r\n\tpath: string;\r\n} {\r\n\tconst metadata = match.metadata ?? {};\r\n\tconst namespace =\r\n\t\tmatch.namespace ?? (typeof metadata[RESERVED_METADATA_KEYS.pointer] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.pointer]).split(\"/\")[0] : \"main\");\r\n\tconst logicalId =\r\n\t\ttypeof metadata[RESERVED_METADATA_KEYS.logicalId] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.logicalId]) : String(match.id ?? \"\");\r\n\tconst userMetadata = Object.fromEntries(Object.entries(metadata).filter(([key]) => !key.startsWith(RESERVED_METADATA_PREFIX)));\r\n\treturn {\r\n\t\tlogicalId,\r\n\t\tvectorId: String(match.id ?? logicalId),\r\n\t\tnamespace,\r\n\t\ttitle: typeof metadata[RESERVED_METADATA_KEYS.title] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.title]) : undefined,\r\n\t\ttext: typeof metadata[RESERVED_METADATA_KEYS.text] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.text]) : undefined,\r\n\t\tmetadata: userMetadata,\r\n\t\tsource: typeof metadata[RESERVED_METADATA_KEYS.source] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.source]) : undefined,\r\n\t\tcreatedAt: typeof metadata[RESERVED_METADATA_KEYS.createdAt] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.createdAt]) : undefined,\r\n\t\tupdatedAt: typeof metadata[RESERVED_METADATA_KEYS.updatedAt] === \"string\" ? String(metadata[RESERVED_METADATA_KEYS.updatedAt]) : undefined,\r\n\t\tpath: buildVirtualPath(namespace, logicalId),\r\n\t};\r\n}\r\n"],"mappings":"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"}
|
package/dist/service.js
CHANGED
|
@@ -3,16 +3,16 @@ import { isCloudflareNotFoundError as n } from "./cloudflare-api.js";
|
|
|
3
3
|
import { CompanionStore as r } from "./companion-store.js";
|
|
4
4
|
import { runDoctor as i } from "./doctor.js";
|
|
5
5
|
import { WorkersAiEmbeddingsClient as a } from "./embeddings-client.js";
|
|
6
|
-
import {
|
|
7
|
-
import { VectorizeClient as
|
|
8
|
-
import { randomUUID as
|
|
6
|
+
import { buildVectorId as o, hydrateInlineRecord as s, mapRecordForUpsert as c } from "./record-mapper.js";
|
|
7
|
+
import { VectorizeClient as l } from "./vectorize-client.js";
|
|
8
|
+
import { randomUUID as u } from "node:crypto";
|
|
9
9
|
//#region src/service.ts
|
|
10
|
-
var
|
|
10
|
+
var d = 3e4, f = 1e3, p = class {
|
|
11
11
|
embeddings;
|
|
12
12
|
vectorize;
|
|
13
13
|
companionStore;
|
|
14
14
|
constructor(e, t) {
|
|
15
|
-
this.config = e, this.openClawConfig = t, this.embeddings = new a(e), this.vectorize = new
|
|
15
|
+
this.config = e, this.openClawConfig = t, this.embeddings = new a(e), this.vectorize = new l(e), this.companionStore = new r(e.companionStorePath);
|
|
16
16
|
}
|
|
17
17
|
resolveNamespace(t) {
|
|
18
18
|
return e({
|
|
@@ -30,7 +30,7 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
30
30
|
filter: e.filter
|
|
31
31
|
});
|
|
32
32
|
return (await Promise.all(r.map(async (e) => {
|
|
33
|
-
let t =
|
|
33
|
+
let t = s(e), n = t.text ?? (await this.companionStore.get(t.namespace, t.logicalId))?.text ?? "";
|
|
34
34
|
return {
|
|
35
35
|
...t,
|
|
36
36
|
text: n,
|
|
@@ -39,9 +39,9 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
39
39
|
}))).filter((t) => t.score >= (e.minScore ?? this.config.minScore));
|
|
40
40
|
}
|
|
41
41
|
async get(e) {
|
|
42
|
-
let t =
|
|
42
|
+
let t = o(this.resolveNamespace(e), e.id), [n] = await this.vectorize.getByIds([t]);
|
|
43
43
|
if (!n) return null;
|
|
44
|
-
let r =
|
|
44
|
+
let r = s(n), i = await this.companionStore.get(r.namespace, r.logicalId);
|
|
45
45
|
return {
|
|
46
46
|
...r,
|
|
47
47
|
text: r.text ?? i?.text ?? ""
|
|
@@ -53,25 +53,34 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
53
53
|
sessionKey: e.sessionKey,
|
|
54
54
|
agentId: e.agentId,
|
|
55
55
|
workspaceDir: e.workspaceDir
|
|
56
|
-
}), n = await this.embeddings.embedQuery(e.input.text), r =
|
|
56
|
+
}), n = await this.embeddings.embedQuery(e.input.text), r = c({
|
|
57
57
|
input: e.input,
|
|
58
58
|
namespace: t,
|
|
59
59
|
embedding: n,
|
|
60
60
|
config: this.config
|
|
61
61
|
});
|
|
62
62
|
r.companionRecord && await this.companionStore.upsert(r.companionRecord);
|
|
63
|
-
let i = await this.vectorize.upsert([r.vector])
|
|
63
|
+
let i = await this.vectorize.upsert([r.vector]), a = await this.get({
|
|
64
|
+
id: r.logicalId,
|
|
65
|
+
namespace: t
|
|
66
|
+
}), o = r.companionRecord === void 0 ? this.fromInlineFallback(r.vector) : this.fromCompanionFallback(r.companionRecord, r.logicalId, t, r.path);
|
|
64
67
|
return {
|
|
65
|
-
...
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
...a ? {
|
|
69
|
+
...o,
|
|
70
|
+
...a,
|
|
71
|
+
title: a.title ?? o.title,
|
|
72
|
+
text: a.text || o.text,
|
|
73
|
+
metadata: Object.keys(a.metadata).length > 0 ? a.metadata : o.metadata,
|
|
74
|
+
source: a.source ?? o.source,
|
|
75
|
+
createdAt: a.createdAt ?? o.createdAt,
|
|
76
|
+
updatedAt: a.updatedAt ?? o.updatedAt
|
|
77
|
+
} : o,
|
|
69
78
|
mutationId: i
|
|
70
79
|
};
|
|
71
80
|
}
|
|
72
81
|
async delete(e) {
|
|
73
82
|
let t = this.resolveNamespace(e);
|
|
74
|
-
return await this.companionStore.delete(t, e.id), this.vectorize.deleteByIds([
|
|
83
|
+
return await this.companionStore.delete(t, e.id), this.vectorize.deleteByIds([o(t, e.id)]);
|
|
75
84
|
}
|
|
76
85
|
async doctor(e) {
|
|
77
86
|
return i({
|
|
@@ -204,7 +213,7 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
204
213
|
status: "pass",
|
|
205
214
|
message: "Embedding dimensions match the Vectorize index."
|
|
206
215
|
});
|
|
207
|
-
let a = t(`cf-memory-test-${
|
|
216
|
+
let a = t(`cf-memory-test-${u()}`), o = `cf-memory-test-${u()}`, s = `OpenClaw Cloudflare memory smoke test ${o}`, c = !1;
|
|
208
217
|
try {
|
|
209
218
|
await this.upsert({ input: {
|
|
210
219
|
id: o,
|
|
@@ -224,13 +233,13 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
224
233
|
query: s,
|
|
225
234
|
namespace: a,
|
|
226
235
|
logicalId: o,
|
|
227
|
-
timeoutMs: e?.timeoutMs ??
|
|
228
|
-
pollIntervalMs: e?.pollIntervalMs ??
|
|
236
|
+
timeoutMs: e?.timeoutMs ?? d,
|
|
237
|
+
pollIntervalMs: e?.pollIntervalMs ?? f
|
|
229
238
|
});
|
|
230
239
|
n.push({
|
|
231
240
|
name: "probe-search",
|
|
232
241
|
status: t ? "pass" : "fail",
|
|
233
|
-
message: t ? "Semantic search returned the smoke-test record." : `Semantic search did not return the smoke-test record within ${(e?.timeoutMs ??
|
|
242
|
+
message: t ? "Semantic search returned the smoke-test record." : `Semantic search did not return the smoke-test record within ${(e?.timeoutMs ?? d) / 1e3} seconds.`
|
|
234
243
|
});
|
|
235
244
|
} catch (e) {
|
|
236
245
|
n.push({
|
|
@@ -281,7 +290,7 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
281
290
|
fromCompanionFallback(e, t, n, r) {
|
|
282
291
|
return {
|
|
283
292
|
logicalId: t,
|
|
284
|
-
vectorId:
|
|
293
|
+
vectorId: o(n, t),
|
|
285
294
|
namespace: n,
|
|
286
295
|
title: e?.title,
|
|
287
296
|
text: e?.text ?? "",
|
|
@@ -292,6 +301,13 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
292
301
|
path: r
|
|
293
302
|
};
|
|
294
303
|
}
|
|
304
|
+
fromInlineFallback(e) {
|
|
305
|
+
let t = s(e);
|
|
306
|
+
return {
|
|
307
|
+
...t,
|
|
308
|
+
text: t.text ?? ""
|
|
309
|
+
};
|
|
310
|
+
}
|
|
295
311
|
async waitForSearchHit(e) {
|
|
296
312
|
let t = Date.now() + e.timeoutMs;
|
|
297
313
|
for (; Date.now() <= t;) {
|
|
@@ -311,6 +327,6 @@ var u = 12e3, d = 1e3, f = class {
|
|
|
311
327
|
}
|
|
312
328
|
};
|
|
313
329
|
//#endregion
|
|
314
|
-
export {
|
|
330
|
+
export { p as CloudflareMemoryService };
|
|
315
331
|
|
|
316
332
|
//# sourceMappingURL=service.js.map
|
package/dist/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../src/service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { OpenClawConfig } from \"openclaw/plugin-sdk/config-runtime\";\nimport { isCloudflareNotFoundError } from \"./cloudflare-api.js\";\nimport { CompanionStore } from \"./companion-store.js\";\nimport { runDoctor } from \"./doctor.js\";\nimport { WorkersAiEmbeddingsClient } from \"./embeddings-client.js\";\nimport { sanitizeNamespace, resolveDefaultNamespace } from \"./namespace.js\";\nimport { hydrateInlineRecord, mapRecordForUpsert } from \"./record-mapper.js\";\nimport type {\n\tCompanionRecord,\n\tDoctorCheck,\n\tDoctorReport,\n\tEmbeddingDimensionsInspection,\n\tHydratedMemoryRecord,\n\tIndexInitializationReport,\n\tMemoryRecordInput,\n\tMetadataFilter,\n\tResolvedPluginConfig,\n\tSmokeTestReport,\n\tUpsertedMemoryRecord,\n\tVectorizeIndexDescription,\n} from \"./types.js\";\nimport { VectorizeClient } from \"./vectorize-client.js\";\n\nconst SMOKE_TEST_TIMEOUT_MS = 12_000;\nconst SMOKE_TEST_POLL_INTERVAL_MS = 1_000;\n\nexport class CloudflareMemoryService {\n\treadonly embeddings: WorkersAiEmbeddingsClient;\n\treadonly vectorize: VectorizeClient;\n\treadonly companionStore: CompanionStore;\n\n\tconstructor(\n\t\treadonly config: ResolvedPluginConfig,\n\t\treadonly openClawConfig: OpenClawConfig,\n\t) {\n\t\tthis.embeddings = new WorkersAiEmbeddingsClient(config);\n\t\tthis.vectorize = new VectorizeClient(config);\n\t\tthis.companionStore = new CompanionStore(config.companionStorePath);\n\t}\n\n\tresolveNamespace(params: { namespace?: string; sessionKey?: string; agentId?: string; workspaceDir?: string }): string {\n\t\treturn resolveDefaultNamespace({\n\t\t\tfixedNamespace: params.namespace ?? this.config.fixedNamespace,\n\t\t\tsessionKey: params.sessionKey,\n\t\t\tagentId: params.agentId,\n\t\t\tworkspaceDir: params.workspaceDir,\n\t\t});\n\t}\n\n\tasync search(params: {\n\t\tquery: string;\n\t\tnamespace?: string;\n\t\tmaxResults?: number;\n\t\tminScore?: number;\n\t\tfilter?: MetadataFilter;\n\t\tsessionKey?: string;\n\t\tagentId?: string;\n\t\tworkspaceDir?: string;\n\t}): Promise<Array<HydratedMemoryRecord & { score: number }>> {\n\t\tconst namespace = this.resolveNamespace(params);\n\t\tconst vector = await this.embeddings.embedQuery(params.query);\n\t\tconst matches = await this.vectorize.query({\n\t\t\tvector,\n\t\t\tnamespace,\n\t\t\ttopK: params.maxResults ?? this.config.topK,\n\t\t\tfilter: params.filter,\n\t\t});\n\n\t\tconst hydrated = await Promise.all(\n\t\t\tmatches.map(async (match) => {\n\t\t\t\tconst base = hydrateInlineRecord(match);\n\t\t\t\tconst text = base.text ?? (await this.companionStore.get(base.namespace, base.logicalId))?.text ?? \"\";\n\t\t\t\treturn {\n\t\t\t\t\t...base,\n\t\t\t\t\ttext,\n\t\t\t\t\tscore: match.score ?? 0,\n\t\t\t\t};\n\t\t\t}),\n\t\t);\n\n\t\treturn hydrated.filter((record) => record.score >= (params.minScore ?? this.config.minScore));\n\t}\n\n\tasync get(params: { id: string; namespace?: string; sessionKey?: string; agentId?: string; workspaceDir?: string }): Promise<HydratedMemoryRecord | null> {\n\t\tconst namespace = this.resolveNamespace(params);\n\t\tconst vectorId = `${namespace}::${params.id}`;\n\t\tconst [match] = await this.vectorize.getByIds([vectorId]);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\tconst base = hydrateInlineRecord(match);\n\t\tconst companion = await this.companionStore.get(base.namespace, base.logicalId);\n\t\treturn {\n\t\t\t...base,\n\t\t\ttext: base.text ?? companion?.text ?? \"\",\n\t\t};\n\t}\n\n\tasync upsert(params: { input: MemoryRecordInput; sessionKey?: string; agentId?: string; workspaceDir?: string }): Promise<UpsertedMemoryRecord> {\n\t\tconst namespace = this.resolveNamespace({\n\t\t\tnamespace: params.input.namespace,\n\t\t\tsessionKey: params.sessionKey,\n\t\t\tagentId: params.agentId,\n\t\t\tworkspaceDir: params.workspaceDir,\n\t\t});\n\t\tconst embedding = await this.embeddings.embedQuery(params.input.text);\n\t\tconst mapped = mapRecordForUpsert({\n\t\t\tinput: params.input,\n\t\t\tnamespace,\n\t\t\tembedding,\n\t\t\tconfig: this.config,\n\t\t});\n\n\t\tif (mapped.companionRecord) {\n\t\t\tawait this.companionStore.upsert(mapped.companionRecord);\n\t\t}\n\n\t\tconst mutationId = await this.vectorize.upsert([mapped.vector]);\n\t\tconst hydrated = await this.get({\n\t\t\tid: mapped.logicalId,\n\t\t\tnamespace,\n\t\t});\n\n\t\treturn {\n\t\t\t...(hydrated ?? this.fromCompanionFallback(mapped.companionRecord, mapped.logicalId, namespace, mapped.path)),\n\t\t\tmutationId,\n\t\t};\n\t}\n\n\tasync delete(params: { id: string; namespace?: string; sessionKey?: string; agentId?: string; workspaceDir?: string }): Promise<string | undefined> {\n\t\tconst namespace = this.resolveNamespace(params);\n\t\tawait this.companionStore.delete(namespace, params.id);\n\t\treturn this.vectorize.deleteByIds([`${namespace}::${params.id}`]);\n\t}\n\n\tasync doctor(options: { createIndexIfMissing?: boolean }): Promise<DoctorReport> {\n\t\treturn runDoctor({\n\t\t\tservice: this,\n\t\t\tcreateIndexIfMissing: options.createIndexIfMissing ?? false,\n\t\t});\n\t}\n\n\tasync inspectEmbeddingDimensions(): Promise<EmbeddingDimensionsInspection> {\n\t\tconst embeddingDimensions = await this.embeddings.probeDimensions();\n\t\tconst configuredDimensions = this.config.createIndex.dimensions;\n\t\treturn {\n\t\t\tembeddingDimensions,\n\t\t\tconfiguredDimensions,\n\t\t\tconfiguredDimensionsMatchModel: configuredDimensions === undefined || configuredDimensions === embeddingDimensions,\n\t\t\ttargetDimensions: embeddingDimensions,\n\t\t};\n\t}\n\n\tasync describeIndexIfExists(): Promise<VectorizeIndexDescription | null> {\n\t\ttry {\n\t\t\treturn await this.vectorize.describeIndex();\n\t\t} catch (error) {\n\t\t\tif (isCloudflareNotFoundError(error)) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tasync initializeIndex(options?: { recreateIfDimensionMismatch?: boolean }): Promise<IndexInitializationReport> {\n\t\tconst recreateIfDimensionMismatch = options?.recreateIfDimensionMismatch ?? true;\n\t\tconst checks: DoctorCheck[] = [];\n\t\tchecks.push({\n\t\t\tname: \"credentials\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Using Cloudflare account ${this.config.accountId} and Vectorize index ${this.config.indexName}.`,\n\t\t});\n\n\t\tconst embedding = await this.inspectEmbeddingDimensions();\n\t\tchecks.push({\n\t\t\tname: \"workers-ai-embeddings\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Workers AI model ${this.config.model} returned ${embedding.embeddingDimensions} dimensions.`,\n\t\t});\n\t\tif (embedding.configuredDimensions !== undefined) {\n\t\t\tchecks.push({\n\t\t\t\tname: \"create-index-dimensions\",\n\t\t\t\tstatus: embedding.configuredDimensionsMatchModel ? \"pass\" : \"warn\",\n\t\t\t\tmessage: embedding.configuredDimensionsMatchModel\n\t\t\t\t\t? `Configured createIndex.dimensions matches the embedding model (${embedding.embeddingDimensions}).`\n\t\t\t\t\t: `Configured createIndex.dimensions (${embedding.configuredDimensions}) does not match the embedding model (${embedding.embeddingDimensions}). Using the live embedding dimensions for initialization.`,\n\t\t\t});\n\t\t}\n\n\t\tconst existingIndex = await this.describeIndexIfExists();\n\t\tlet created = false;\n\t\tlet recreated = false;\n\t\tlet indexDimensions = existingIndex?.config.dimensions;\n\n\t\tif (!existingIndex) {\n\t\t\tconst createdIndex = await this.vectorize.createIndex(embedding.targetDimensions);\n\t\t\tcreated = true;\n\t\t\tindexDimensions = createdIndex.config.dimensions;\n\t\t\tchecks.push({\n\t\t\t\tname: \"vectorize-index\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Created Vectorize index \"${this.config.indexName}\" with ${indexDimensions} dimensions.`,\n\t\t\t});\n\t\t} else if (existingIndex.config.dimensions === embedding.targetDimensions) {\n\t\t\tchecks.push({\n\t\t\t\tname: \"vectorize-index\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" already uses ${existingIndex.config.dimensions} dimensions.`,\n\t\t\t});\n\t\t} else if (!recreateIfDimensionMismatch) {\n\t\t\tchecks.push({\n\t\t\t\tname: \"vectorize-index\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" uses ${existingIndex.config.dimensions} dimensions, but the embedding model requires ${embedding.targetDimensions}. Recreate the index or rerun init with recreation enabled.`,\n\t\t\t});\n\t\t} else {\n\t\t\tawait this.vectorize.deleteIndex();\n\t\t\tconst recreatedIndex = await this.vectorize.createIndex(embedding.targetDimensions);\n\t\t\trecreated = true;\n\t\t\tindexDimensions = recreatedIndex.config.dimensions;\n\t\t\tchecks.push({\n\t\t\t\tname: \"vectorize-index\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Recreated Vectorize index \"${this.config.indexName}\" from ${existingIndex.config.dimensions} to ${indexDimensions} dimensions.`,\n\t\t\t});\n\t\t}\n\n\t\tchecks.push({\n\t\t\tname: \"dimension-match\",\n\t\t\tstatus: indexDimensions === embedding.embeddingDimensions ? \"pass\" : \"fail\",\n\t\t\tmessage:\n\t\t\t\tindexDimensions === embedding.embeddingDimensions\n\t\t\t\t\t? \"Embedding dimensions match the Vectorize index.\"\n\t\t\t\t\t: `Embedding dimensions (${embedding.embeddingDimensions}) do not match the Vectorize index dimensions (${indexDimensions ?? \"unknown\"}).`,\n\t\t});\n\t\tchecks.push({\n\t\t\tname: \"metadata-filters\",\n\t\t\tstatus: this.config.metadataIndexedFields.length > 0 ? \"pass\" : \"warn\",\n\t\t\tmessage:\n\t\t\t\tthis.config.metadataIndexedFields.length > 0\n\t\t\t\t\t? `Configured metadata-index guidance for: ${this.config.metadataIndexedFields.join(\", \")}.`\n\t\t\t\t\t: \"No metadataIndexedFields configured. Add metadata indexes in Cloudflare before relying on filter-heavy queries.\",\n\t\t});\n\n\t\treturn {\n\t\t\tok: checks.every((check) => check.status !== \"fail\"),\n\t\t\tchecks,\n\t\t\tcreated,\n\t\t\trecreated,\n\t\t\tembeddingDimensions: embedding.embeddingDimensions,\n\t\t\tindexDimensions,\n\t\t};\n\t}\n\n\tasync runSmokeTest(options?: { timeoutMs?: number; pollIntervalMs?: number }): Promise<SmokeTestReport> {\n\t\tconst checks: DoctorCheck[] = [];\n\t\tchecks.push({\n\t\t\tname: \"credentials\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Using Cloudflare account ${this.config.accountId} and Vectorize index ${this.config.indexName}.`,\n\t\t});\n\n\t\tconst embedding = await this.inspectEmbeddingDimensions();\n\t\tchecks.push({\n\t\t\tname: \"workers-ai-embeddings\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Workers AI model ${this.config.model} returned ${embedding.embeddingDimensions} dimensions.`,\n\t\t});\n\t\tif (embedding.configuredDimensions !== undefined) {\n\t\t\tchecks.push({\n\t\t\t\tname: \"create-index-dimensions\",\n\t\t\t\tstatus: embedding.configuredDimensionsMatchModel ? \"pass\" : \"warn\",\n\t\t\t\tmessage: embedding.configuredDimensionsMatchModel\n\t\t\t\t\t? `Configured createIndex.dimensions matches the embedding model (${embedding.embeddingDimensions}).`\n\t\t\t\t\t: `Configured createIndex.dimensions (${embedding.configuredDimensions}) does not match the embedding model (${embedding.embeddingDimensions}). Using the live embedding dimensions for validation.`,\n\t\t\t});\n\t\t}\n\n\t\tconst existingIndex = await this.describeIndexIfExists();\n\t\tif (!existingIndex) {\n\t\t\tchecks.push({\n\t\t\t\tname: \"vectorize-index\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" was not found. Run \"openclaw cf-memory init\" before rerunning this test.`,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tchecks,\n\t\t\t\tnamespace: \"n/a\",\n\t\t\t\tlogicalId: \"n/a\",\n\t\t\t};\n\t\t}\n\n\t\tchecks.push({\n\t\t\tname: \"vectorize-index\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" is reachable.`,\n\t\t});\n\t\tif (existingIndex.config.dimensions !== embedding.embeddingDimensions) {\n\t\t\tchecks.push({\n\t\t\t\tname: \"dimension-match\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: `Embedding dimensions (${embedding.embeddingDimensions}) do not match the Vectorize index dimensions (${existingIndex.config.dimensions}). Run \"openclaw cf-memory init\" to repair the index.`,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tchecks,\n\t\t\t\tnamespace: \"n/a\",\n\t\t\t\tlogicalId: \"n/a\",\n\t\t\t};\n\t\t}\n\n\t\tchecks.push({\n\t\t\tname: \"dimension-match\",\n\t\t\tstatus: \"pass\",\n\t\t\tmessage: \"Embedding dimensions match the Vectorize index.\",\n\t\t});\n\n\t\tconst namespace = sanitizeNamespace(`cf-memory-test-${randomUUID()}`);\n\t\tconst logicalId = `cf-memory-test-${randomUUID()}`;\n\t\tconst probeText = `OpenClaw Cloudflare memory smoke test ${logicalId}`;\n\t\tlet probeUpserted = false;\n\n\t\ttry {\n\t\t\tawait this.upsert({\n\t\t\t\tinput: {\n\t\t\t\t\tid: logicalId,\n\t\t\t\t\tnamespace,\n\t\t\t\t\ttext: probeText,\n\t\t\t\t\tsource: \"cf-memory-test\",\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\tprobe: true,\n\t\t\t\t\t\tprobeId: logicalId,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t\tprobeUpserted = true;\n\t\t\tchecks.push({\n\t\t\t\tname: \"probe-upsert\",\n\t\t\t\tstatus: \"pass\",\n\t\t\t\tmessage: `Inserted smoke-test record ${logicalId} in namespace ${namespace}.`,\n\t\t\t});\n\n\t\t\tconst found = await this.waitForSearchHit({\n\t\t\t\tquery: probeText,\n\t\t\t\tnamespace,\n\t\t\t\tlogicalId,\n\t\t\t\ttimeoutMs: options?.timeoutMs ?? SMOKE_TEST_TIMEOUT_MS,\n\t\t\t\tpollIntervalMs: options?.pollIntervalMs ?? SMOKE_TEST_POLL_INTERVAL_MS,\n\t\t\t});\n\t\t\tchecks.push({\n\t\t\t\tname: \"probe-search\",\n\t\t\t\tstatus: found ? \"pass\" : \"fail\",\n\t\t\t\tmessage: found\n\t\t\t\t\t? \"Semantic search returned the smoke-test record.\"\n\t\t\t\t\t: `Semantic search did not return the smoke-test record within ${(options?.timeoutMs ?? SMOKE_TEST_TIMEOUT_MS) / 1000} seconds.`,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tchecks.push({\n\t\t\t\tname: probeUpserted ? \"probe-search\" : \"probe-upsert\",\n\t\t\t\tstatus: \"fail\",\n\t\t\t\tmessage: error instanceof Error ? error.message : \"Smoke test failed with an unknown error.\",\n\t\t\t});\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait this.delete({ id: logicalId, namespace });\n\t\t\t\tchecks.push({\n\t\t\t\t\tname: \"probe-cleanup\",\n\t\t\t\t\tstatus: \"pass\",\n\t\t\t\t\tmessage: `Deleted smoke-test record ${logicalId}.`,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tchecks.push({\n\t\t\t\t\tname: \"probe-cleanup\",\n\t\t\t\t\tstatus: \"fail\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : `Failed to delete smoke-test record ${logicalId}.`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tok: checks.every((check) => check.status !== \"fail\"),\n\t\t\tchecks,\n\t\t\tnamespace,\n\t\t\tlogicalId,\n\t\t};\n\t}\n\n\tasync ensureIndexExists(createIfMissing: boolean, targetDimensions?: number): Promise<{ created: boolean; dimensions: number }> {\n\t\ttry {\n\t\t\tconst description = await this.vectorize.describeIndex();\n\t\t\treturn {\n\t\t\t\tcreated: false,\n\t\t\t\tdimensions: description.config.dimensions,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (!createIfMissing || !isCloudflareNotFoundError(error)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst dimensions = targetDimensions ?? (await this.inspectEmbeddingDimensions()).targetDimensions;\n\t\t\tconst createdIndex = await this.vectorize.createIndex(dimensions);\n\t\t\treturn {\n\t\t\t\tcreated: true,\n\t\t\t\tdimensions: createdIndex.config.dimensions,\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate fromCompanionFallback(companionRecord: CompanionRecord | undefined, logicalId: string, namespace: string, path: string): UpsertedMemoryRecord {\n\t\treturn {\n\t\t\tlogicalId,\n\t\t\tvectorId: `${namespace}::${logicalId}`,\n\t\t\tnamespace,\n\t\t\ttitle: companionRecord?.title,\n\t\t\ttext: companionRecord?.text ?? \"\",\n\t\t\tmetadata: companionRecord?.metadata ?? {},\n\t\t\tsource: companionRecord?.source,\n\t\t\tcreatedAt: companionRecord?.createdAt,\n\t\t\tupdatedAt: companionRecord?.updatedAt,\n\t\t\tpath,\n\t\t};\n\t}\n\n\tprivate async waitForSearchHit(params: { query: string; namespace: string; logicalId: string; timeoutMs: number; pollIntervalMs: number }): Promise<boolean> {\n\t\tconst deadline = Date.now() + params.timeoutMs;\n\t\twhile (Date.now() <= deadline) {\n\t\t\tconst results = await this.search({\n\t\t\t\tquery: params.query,\n\t\t\t\tnamespace: params.namespace,\n\t\t\t\tmaxResults: 5,\n\t\t\t\tminScore: 0,\n\t\t\t});\n\t\t\tif (results.some((record) => record.logicalId === params.logicalId)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (Date.now() + params.pollIntervalMs > deadline) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tawait this.pause(params.pollIntervalMs);\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate async pause(ms: number): Promise<void> {\n\t\tawait new Promise((resolve) => setTimeout(resolve, ms));\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../src/service.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\r\nimport type { OpenClawConfig } from \"openclaw/plugin-sdk/config-runtime\";\r\nimport { isCloudflareNotFoundError } from \"./cloudflare-api.js\";\r\nimport { CompanionStore } from \"./companion-store.js\";\r\nimport { runDoctor } from \"./doctor.js\";\r\nimport { WorkersAiEmbeddingsClient } from \"./embeddings-client.js\";\r\nimport { sanitizeNamespace, resolveDefaultNamespace } from \"./namespace.js\";\r\nimport { buildVectorId, hydrateInlineRecord, mapRecordForUpsert } from \"./record-mapper.js\";\r\nimport type {\r\n\tCompanionRecord,\r\n\tDoctorCheck,\r\n\tDoctorReport,\r\n\tEmbeddingDimensionsInspection,\r\n\tHydratedMemoryRecord,\r\n\tIndexInitializationReport,\r\n\tMemoryRecordInput,\r\n\tMetadataFilter,\r\n\tResolvedPluginConfig,\r\n\tSmokeTestReport,\r\n\tUpsertedMemoryRecord,\r\n\tVectorizeIndexDescription,\r\n} from \"./types.js\";\r\nimport { VectorizeClient } from \"./vectorize-client.js\";\r\n\r\nconst SMOKE_TEST_TIMEOUT_MS = 30_000;\r\nconst SMOKE_TEST_POLL_INTERVAL_MS = 1_000;\r\n\r\nexport class CloudflareMemoryService {\r\n\treadonly embeddings: WorkersAiEmbeddingsClient;\r\n\treadonly vectorize: VectorizeClient;\r\n\treadonly companionStore: CompanionStore;\r\n\r\n\tconstructor(\r\n\t\treadonly config: ResolvedPluginConfig,\r\n\t\treadonly openClawConfig: OpenClawConfig,\r\n\t) {\r\n\t\tthis.embeddings = new WorkersAiEmbeddingsClient(config);\r\n\t\tthis.vectorize = new VectorizeClient(config);\r\n\t\tthis.companionStore = new CompanionStore(config.companionStorePath);\r\n\t}\r\n\r\n\tresolveNamespace(params: { namespace?: string; sessionKey?: string; agentId?: string; workspaceDir?: string }): string {\r\n\t\treturn resolveDefaultNamespace({\r\n\t\t\tfixedNamespace: params.namespace ?? this.config.fixedNamespace,\r\n\t\t\tsessionKey: params.sessionKey,\r\n\t\t\tagentId: params.agentId,\r\n\t\t\tworkspaceDir: params.workspaceDir,\r\n\t\t});\r\n\t}\r\n\r\n\tasync search(params: {\r\n\t\tquery: string;\r\n\t\tnamespace?: string;\r\n\t\tmaxResults?: number;\r\n\t\tminScore?: number;\r\n\t\tfilter?: MetadataFilter;\r\n\t\tsessionKey?: string;\r\n\t\tagentId?: string;\r\n\t\tworkspaceDir?: string;\r\n\t}): Promise<Array<HydratedMemoryRecord & { score: number }>> {\r\n\t\tconst namespace = this.resolveNamespace(params);\r\n\t\tconst vector = await this.embeddings.embedQuery(params.query);\r\n\t\tconst matches = await this.vectorize.query({\r\n\t\t\tvector,\r\n\t\t\tnamespace,\r\n\t\t\ttopK: params.maxResults ?? this.config.topK,\r\n\t\t\tfilter: params.filter,\r\n\t\t});\r\n\r\n\t\tconst hydrated = await Promise.all(\r\n\t\t\tmatches.map(async (match) => {\r\n\t\t\t\tconst base = hydrateInlineRecord(match);\r\n\t\t\t\tconst text = base.text ?? (await this.companionStore.get(base.namespace, base.logicalId))?.text ?? \"\";\r\n\t\t\t\treturn {\r\n\t\t\t\t\t...base,\r\n\t\t\t\t\ttext,\r\n\t\t\t\t\tscore: match.score ?? 0,\r\n\t\t\t\t};\r\n\t\t\t}),\r\n\t\t);\r\n\r\n\t\treturn hydrated.filter((record) => record.score >= (params.minScore ?? this.config.minScore));\r\n\t}\r\n\r\n\tasync get(params: { id: string; namespace?: string; sessionKey?: string; agentId?: string; workspaceDir?: string }): Promise<HydratedMemoryRecord | null> {\r\n\t\tconst namespace = this.resolveNamespace(params);\r\n\t\tconst vectorId = buildVectorId(namespace, params.id);\r\n\t\tconst [match] = await this.vectorize.getByIds([vectorId]);\r\n\t\tif (!match) {\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tconst base = hydrateInlineRecord(match);\r\n\t\tconst companion = await this.companionStore.get(base.namespace, base.logicalId);\r\n\t\treturn {\r\n\t\t\t...base,\r\n\t\t\ttext: base.text ?? companion?.text ?? \"\",\r\n\t\t};\r\n\t}\r\n\r\n\tasync upsert(params: { input: MemoryRecordInput; sessionKey?: string; agentId?: string; workspaceDir?: string }): Promise<UpsertedMemoryRecord> {\r\n\t\tconst namespace = this.resolveNamespace({\r\n\t\t\tnamespace: params.input.namespace,\r\n\t\t\tsessionKey: params.sessionKey,\r\n\t\t\tagentId: params.agentId,\r\n\t\t\tworkspaceDir: params.workspaceDir,\r\n\t\t});\r\n\t\tconst embedding = await this.embeddings.embedQuery(params.input.text);\r\n\t\tconst mapped = mapRecordForUpsert({\r\n\t\t\tinput: params.input,\r\n\t\t\tnamespace,\r\n\t\t\tembedding,\r\n\t\t\tconfig: this.config,\r\n\t\t});\r\n\r\n\t\tif (mapped.companionRecord) {\r\n\t\t\tawait this.companionStore.upsert(mapped.companionRecord);\r\n\t\t}\r\n\r\n\t\tconst mutationId = await this.vectorize.upsert([mapped.vector]);\r\n\t\tconst hydrated = await this.get({\r\n\t\t\tid: mapped.logicalId,\r\n\t\t\tnamespace,\r\n\t\t});\r\n\t\tconst fallback =\r\n\t\t\tmapped.companionRecord === undefined\r\n\t\t\t\t? this.fromInlineFallback(mapped.vector)\r\n\t\t\t\t: this.fromCompanionFallback(mapped.companionRecord, mapped.logicalId, namespace, mapped.path);\r\n\t\tconst resolved = hydrated\r\n\t\t\t? {\r\n\t\t\t\t\t...fallback,\r\n\t\t\t\t\t...hydrated,\r\n\t\t\t\t\ttitle: hydrated.title ?? fallback.title,\r\n\t\t\t\t\ttext: hydrated.text || fallback.text,\r\n\t\t\t\t\tmetadata: Object.keys(hydrated.metadata).length > 0 ? hydrated.metadata : fallback.metadata,\r\n\t\t\t\t\tsource: hydrated.source ?? fallback.source,\r\n\t\t\t\t\tcreatedAt: hydrated.createdAt ?? fallback.createdAt,\r\n\t\t\t\t\tupdatedAt: hydrated.updatedAt ?? fallback.updatedAt,\r\n\t\t\t }\r\n\t\t\t: fallback;\r\n\r\n\t\treturn {\r\n\t\t\t...resolved,\r\n\t\t\tmutationId,\r\n\t\t};\r\n\t}\r\n\r\n\tasync delete(params: { id: string; namespace?: string; sessionKey?: string; agentId?: string; workspaceDir?: string }): Promise<string | undefined> {\r\n\t\tconst namespace = this.resolveNamespace(params);\r\n\t\tawait this.companionStore.delete(namespace, params.id);\r\n\t\treturn this.vectorize.deleteByIds([buildVectorId(namespace, params.id)]);\r\n\t}\r\n\r\n\tasync doctor(options: { createIndexIfMissing?: boolean }): Promise<DoctorReport> {\r\n\t\treturn runDoctor({\r\n\t\t\tservice: this,\r\n\t\t\tcreateIndexIfMissing: options.createIndexIfMissing ?? false,\r\n\t\t});\r\n\t}\r\n\r\n\tasync inspectEmbeddingDimensions(): Promise<EmbeddingDimensionsInspection> {\r\n\t\tconst embeddingDimensions = await this.embeddings.probeDimensions();\r\n\t\tconst configuredDimensions = this.config.createIndex.dimensions;\r\n\t\treturn {\r\n\t\t\tembeddingDimensions,\r\n\t\t\tconfiguredDimensions,\r\n\t\t\tconfiguredDimensionsMatchModel: configuredDimensions === undefined || configuredDimensions === embeddingDimensions,\r\n\t\t\ttargetDimensions: embeddingDimensions,\r\n\t\t};\r\n\t}\r\n\r\n\tasync describeIndexIfExists(): Promise<VectorizeIndexDescription | null> {\r\n\t\ttry {\r\n\t\t\treturn await this.vectorize.describeIndex();\r\n\t\t} catch (error) {\r\n\t\t\tif (isCloudflareNotFoundError(error)) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\tasync initializeIndex(options?: { recreateIfDimensionMismatch?: boolean }): Promise<IndexInitializationReport> {\r\n\t\tconst recreateIfDimensionMismatch = options?.recreateIfDimensionMismatch ?? true;\r\n\t\tconst checks: DoctorCheck[] = [];\r\n\t\tchecks.push({\r\n\t\t\tname: \"credentials\",\r\n\t\t\tstatus: \"pass\",\r\n\t\t\tmessage: `Using Cloudflare account ${this.config.accountId} and Vectorize index ${this.config.indexName}.`,\r\n\t\t});\r\n\r\n\t\tconst embedding = await this.inspectEmbeddingDimensions();\r\n\t\tchecks.push({\r\n\t\t\tname: \"workers-ai-embeddings\",\r\n\t\t\tstatus: \"pass\",\r\n\t\t\tmessage: `Workers AI model ${this.config.model} returned ${embedding.embeddingDimensions} dimensions.`,\r\n\t\t});\r\n\t\tif (embedding.configuredDimensions !== undefined) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"create-index-dimensions\",\r\n\t\t\t\tstatus: embedding.configuredDimensionsMatchModel ? \"pass\" : \"warn\",\r\n\t\t\t\tmessage: embedding.configuredDimensionsMatchModel\r\n\t\t\t\t\t? `Configured createIndex.dimensions matches the embedding model (${embedding.embeddingDimensions}).`\r\n\t\t\t\t\t: `Configured createIndex.dimensions (${embedding.configuredDimensions}) does not match the embedding model (${embedding.embeddingDimensions}). Using the live embedding dimensions for initialization.`,\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tconst existingIndex = await this.describeIndexIfExists();\r\n\t\tlet created = false;\r\n\t\tlet recreated = false;\r\n\t\tlet indexDimensions = existingIndex?.config.dimensions;\r\n\r\n\t\tif (!existingIndex) {\r\n\t\t\tconst createdIndex = await this.vectorize.createIndex(embedding.targetDimensions);\r\n\t\t\tcreated = true;\r\n\t\t\tindexDimensions = createdIndex.config.dimensions;\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"vectorize-index\",\r\n\t\t\t\tstatus: \"pass\",\r\n\t\t\t\tmessage: `Created Vectorize index \"${this.config.indexName}\" with ${indexDimensions} dimensions.`,\r\n\t\t\t});\r\n\t\t} else if (existingIndex.config.dimensions === embedding.targetDimensions) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"vectorize-index\",\r\n\t\t\t\tstatus: \"pass\",\r\n\t\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" already uses ${existingIndex.config.dimensions} dimensions.`,\r\n\t\t\t});\r\n\t\t} else if (!recreateIfDimensionMismatch) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"vectorize-index\",\r\n\t\t\t\tstatus: \"fail\",\r\n\t\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" uses ${existingIndex.config.dimensions} dimensions, but the embedding model requires ${embedding.targetDimensions}. Recreate the index or rerun init with recreation enabled.`,\r\n\t\t\t});\r\n\t\t} else {\r\n\t\t\tawait this.vectorize.deleteIndex();\r\n\t\t\tconst recreatedIndex = await this.vectorize.createIndex(embedding.targetDimensions);\r\n\t\t\trecreated = true;\r\n\t\t\tindexDimensions = recreatedIndex.config.dimensions;\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"vectorize-index\",\r\n\t\t\t\tstatus: \"pass\",\r\n\t\t\t\tmessage: `Recreated Vectorize index \"${this.config.indexName}\" from ${existingIndex.config.dimensions} to ${indexDimensions} dimensions.`,\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tchecks.push({\r\n\t\t\tname: \"dimension-match\",\r\n\t\t\tstatus: indexDimensions === embedding.embeddingDimensions ? \"pass\" : \"fail\",\r\n\t\t\tmessage:\r\n\t\t\t\tindexDimensions === embedding.embeddingDimensions\r\n\t\t\t\t\t? \"Embedding dimensions match the Vectorize index.\"\r\n\t\t\t\t\t: `Embedding dimensions (${embedding.embeddingDimensions}) do not match the Vectorize index dimensions (${indexDimensions ?? \"unknown\"}).`,\r\n\t\t});\r\n\t\tchecks.push({\r\n\t\t\tname: \"metadata-filters\",\r\n\t\t\tstatus: this.config.metadataIndexedFields.length > 0 ? \"pass\" : \"warn\",\r\n\t\t\tmessage:\r\n\t\t\t\tthis.config.metadataIndexedFields.length > 0\r\n\t\t\t\t\t? `Configured metadata-index guidance for: ${this.config.metadataIndexedFields.join(\", \")}.`\r\n\t\t\t\t\t: \"No metadataIndexedFields configured. Add metadata indexes in Cloudflare before relying on filter-heavy queries.\",\r\n\t\t});\r\n\r\n\t\treturn {\r\n\t\t\tok: checks.every((check) => check.status !== \"fail\"),\r\n\t\t\tchecks,\r\n\t\t\tcreated,\r\n\t\t\trecreated,\r\n\t\t\tembeddingDimensions: embedding.embeddingDimensions,\r\n\t\t\tindexDimensions,\r\n\t\t};\r\n\t}\r\n\r\n\tasync runSmokeTest(options?: { timeoutMs?: number; pollIntervalMs?: number }): Promise<SmokeTestReport> {\r\n\t\tconst checks: DoctorCheck[] = [];\r\n\t\tchecks.push({\r\n\t\t\tname: \"credentials\",\r\n\t\t\tstatus: \"pass\",\r\n\t\t\tmessage: `Using Cloudflare account ${this.config.accountId} and Vectorize index ${this.config.indexName}.`,\r\n\t\t});\r\n\r\n\t\tconst embedding = await this.inspectEmbeddingDimensions();\r\n\t\tchecks.push({\r\n\t\t\tname: \"workers-ai-embeddings\",\r\n\t\t\tstatus: \"pass\",\r\n\t\t\tmessage: `Workers AI model ${this.config.model} returned ${embedding.embeddingDimensions} dimensions.`,\r\n\t\t});\r\n\t\tif (embedding.configuredDimensions !== undefined) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"create-index-dimensions\",\r\n\t\t\t\tstatus: embedding.configuredDimensionsMatchModel ? \"pass\" : \"warn\",\r\n\t\t\t\tmessage: embedding.configuredDimensionsMatchModel\r\n\t\t\t\t\t? `Configured createIndex.dimensions matches the embedding model (${embedding.embeddingDimensions}).`\r\n\t\t\t\t\t: `Configured createIndex.dimensions (${embedding.configuredDimensions}) does not match the embedding model (${embedding.embeddingDimensions}). Using the live embedding dimensions for validation.`,\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tconst existingIndex = await this.describeIndexIfExists();\r\n\t\tif (!existingIndex) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"vectorize-index\",\r\n\t\t\t\tstatus: \"fail\",\r\n\t\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" was not found. Run \"openclaw cf-memory init\" before rerunning this test.`,\r\n\t\t\t});\r\n\t\t\treturn {\r\n\t\t\t\tok: false,\r\n\t\t\t\tchecks,\r\n\t\t\t\tnamespace: \"n/a\",\r\n\t\t\t\tlogicalId: \"n/a\",\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\tchecks.push({\r\n\t\t\tname: \"vectorize-index\",\r\n\t\t\tstatus: \"pass\",\r\n\t\t\tmessage: `Vectorize index \"${this.config.indexName}\" is reachable.`,\r\n\t\t});\r\n\t\tif (existingIndex.config.dimensions !== embedding.embeddingDimensions) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"dimension-match\",\r\n\t\t\t\tstatus: \"fail\",\r\n\t\t\t\tmessage: `Embedding dimensions (${embedding.embeddingDimensions}) do not match the Vectorize index dimensions (${existingIndex.config.dimensions}). Run \"openclaw cf-memory init\" to repair the index.`,\r\n\t\t\t});\r\n\t\t\treturn {\r\n\t\t\t\tok: false,\r\n\t\t\t\tchecks,\r\n\t\t\t\tnamespace: \"n/a\",\r\n\t\t\t\tlogicalId: \"n/a\",\r\n\t\t\t};\r\n\t\t}\r\n\r\n\t\tchecks.push({\r\n\t\t\tname: \"dimension-match\",\r\n\t\t\tstatus: \"pass\",\r\n\t\t\tmessage: \"Embedding dimensions match the Vectorize index.\",\r\n\t\t});\r\n\r\n\t\tconst namespace = sanitizeNamespace(`cf-memory-test-${randomUUID()}`);\r\n\t\tconst logicalId = `cf-memory-test-${randomUUID()}`;\r\n\t\tconst probeText = `OpenClaw Cloudflare memory smoke test ${logicalId}`;\r\n\t\tlet probeUpserted = false;\r\n\r\n\t\ttry {\r\n\t\t\tawait this.upsert({\r\n\t\t\t\tinput: {\r\n\t\t\t\t\tid: logicalId,\r\n\t\t\t\t\tnamespace,\r\n\t\t\t\t\ttext: probeText,\r\n\t\t\t\t\tsource: \"cf-memory-test\",\r\n\t\t\t\t\tmetadata: {\r\n\t\t\t\t\t\tprobe: true,\r\n\t\t\t\t\t\tprobeId: logicalId,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t});\r\n\t\t\tprobeUpserted = true;\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"probe-upsert\",\r\n\t\t\t\tstatus: \"pass\",\r\n\t\t\t\tmessage: `Inserted smoke-test record ${logicalId} in namespace ${namespace}.`,\r\n\t\t\t});\r\n\r\n\t\t\tconst found = await this.waitForSearchHit({\r\n\t\t\t\tquery: probeText,\r\n\t\t\t\tnamespace,\r\n\t\t\t\tlogicalId,\r\n\t\t\t\ttimeoutMs: options?.timeoutMs ?? SMOKE_TEST_TIMEOUT_MS,\r\n\t\t\t\tpollIntervalMs: options?.pollIntervalMs ?? SMOKE_TEST_POLL_INTERVAL_MS,\r\n\t\t\t});\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: \"probe-search\",\r\n\t\t\t\tstatus: found ? \"pass\" : \"fail\",\r\n\t\t\t\tmessage: found\r\n\t\t\t\t\t? \"Semantic search returned the smoke-test record.\"\r\n\t\t\t\t\t: `Semantic search did not return the smoke-test record within ${(options?.timeoutMs ?? SMOKE_TEST_TIMEOUT_MS) / 1000} seconds.`,\r\n\t\t\t});\r\n\t\t} catch (error) {\r\n\t\t\tchecks.push({\r\n\t\t\t\tname: probeUpserted ? \"probe-search\" : \"probe-upsert\",\r\n\t\t\t\tstatus: \"fail\",\r\n\t\t\t\tmessage: error instanceof Error ? error.message : \"Smoke test failed with an unknown error.\",\r\n\t\t\t});\r\n\t\t} finally {\r\n\t\t\ttry {\r\n\t\t\t\tawait this.delete({ id: logicalId, namespace });\r\n\t\t\t\tchecks.push({\r\n\t\t\t\t\tname: \"probe-cleanup\",\r\n\t\t\t\t\tstatus: \"pass\",\r\n\t\t\t\t\tmessage: `Deleted smoke-test record ${logicalId}.`,\r\n\t\t\t\t});\r\n\t\t\t} catch (error) {\r\n\t\t\t\tchecks.push({\r\n\t\t\t\t\tname: \"probe-cleanup\",\r\n\t\t\t\t\tstatus: \"fail\",\r\n\t\t\t\t\tmessage: error instanceof Error ? error.message : `Failed to delete smoke-test record ${logicalId}.`,\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn {\r\n\t\t\tok: checks.every((check) => check.status !== \"fail\"),\r\n\t\t\tchecks,\r\n\t\t\tnamespace,\r\n\t\t\tlogicalId,\r\n\t\t};\r\n\t}\r\n\r\n\tasync ensureIndexExists(createIfMissing: boolean, targetDimensions?: number): Promise<{ created: boolean; dimensions: number }> {\r\n\t\ttry {\r\n\t\t\tconst description = await this.vectorize.describeIndex();\r\n\t\t\treturn {\r\n\t\t\t\tcreated: false,\r\n\t\t\t\tdimensions: description.config.dimensions,\r\n\t\t\t};\r\n\t\t} catch (error) {\r\n\t\t\tif (!createIfMissing || !isCloudflareNotFoundError(error)) {\r\n\t\t\t\tthrow error;\r\n\t\t\t}\r\n\t\t\tconst dimensions = targetDimensions ?? (await this.inspectEmbeddingDimensions()).targetDimensions;\r\n\t\t\tconst createdIndex = await this.vectorize.createIndex(dimensions);\r\n\t\t\treturn {\r\n\t\t\t\tcreated: true,\r\n\t\t\t\tdimensions: createdIndex.config.dimensions,\r\n\t\t\t};\r\n\t\t}\r\n\t}\r\n\r\n\tprivate fromCompanionFallback(companionRecord: CompanionRecord | undefined, logicalId: string, namespace: string, path: string): UpsertedMemoryRecord {\r\n\t\treturn {\r\n\t\t\tlogicalId,\r\n\t\t\tvectorId: buildVectorId(namespace, logicalId),\r\n\t\t\tnamespace,\r\n\t\t\ttitle: companionRecord?.title,\r\n\t\t\ttext: companionRecord?.text ?? \"\",\r\n\t\t\tmetadata: companionRecord?.metadata ?? {},\r\n\t\t\tsource: companionRecord?.source,\r\n\t\t\tcreatedAt: companionRecord?.createdAt,\r\n\t\t\tupdatedAt: companionRecord?.updatedAt,\r\n\t\t\tpath,\r\n\t\t};\r\n\t}\r\n\r\n\tprivate fromInlineFallback(vector: { id: string; namespace?: string; metadata?: Record<string, string | number | boolean> }): UpsertedMemoryRecord {\r\n\t\tconst record = hydrateInlineRecord(vector);\r\n\t\treturn {\r\n\t\t\t...record,\r\n\t\t\ttext: record.text ?? \"\",\r\n\t\t};\r\n\t}\r\n\r\n\tprivate async waitForSearchHit(params: { query: string; namespace: string; logicalId: string; timeoutMs: number; pollIntervalMs: number }): Promise<boolean> {\r\n\t\tconst deadline = Date.now() + params.timeoutMs;\r\n\t\twhile (Date.now() <= deadline) {\r\n\t\t\tconst results = await this.search({\r\n\t\t\t\tquery: params.query,\r\n\t\t\t\tnamespace: params.namespace,\r\n\t\t\t\tmaxResults: 5,\r\n\t\t\t\tminScore: 0,\r\n\t\t\t});\r\n\t\t\tif (results.some((record) => record.logicalId === params.logicalId)) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\tif (Date.now() + params.pollIntervalMs > deadline) {\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tawait this.pause(params.pollIntervalMs);\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tprivate async pause(ms: number): Promise<void> {\r\n\t\tawait new Promise((resolve) => setTimeout(resolve, ms));\r\n\t}\r\n}\r\n"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
type ResponseMode = "auto" | "envelope";
|
|
1
2
|
export declare function isCloudflareNotFoundError(error: unknown): boolean;
|
|
2
3
|
export declare function requestCloudflare<T>(params: {
|
|
3
4
|
url: string;
|
|
@@ -5,4 +6,6 @@ export declare function requestCloudflare<T>(params: {
|
|
|
5
6
|
method?: string;
|
|
6
7
|
headers?: Record<string, string>;
|
|
7
8
|
body?: string;
|
|
9
|
+
responseMode?: ResponseMode;
|
|
8
10
|
}): Promise<T>;
|
|
11
|
+
export {};
|
package/dist/types/service.d.ts
CHANGED
package/dist/vectorize-client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vectorize-client.js","names":[],"sources":["../src/vectorize-client.ts"],"sourcesContent":["import { requestCloudflare } from \"./cloudflare-api.js\";\nimport type { MetadataFilter, ResolvedPluginConfig, VectorizeIndexDescription, VectorizeQueryMatch, VectorizeVector } from \"./types.js\";\n\ntype MutationResponse = {\n\tmutationId?: string;\n};\n\ntype QueryResponse = {\n\tcount?: number;\n\tmatches?: VectorizeQueryMatch[];\n};\n\nexport class VectorizeClient {\n\tconstructor(private readonly config: ResolvedPluginConfig) {}\n\n\tasync describeIndex(): Promise<VectorizeIndexDescription> {\n\t\treturn requestCloudflare<VectorizeIndexDescription>({\n\t\t\turl: this.config.vectorizeBaseUrl,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\tmethod: \"GET\",\n\t\t});\n\t}\n\n\tasync createIndex(dimensions: number, metric = this.config.createIndex.metric): Promise<VectorizeIndexDescription> {\n\t\treturn requestCloudflare<VectorizeIndexDescription>({\n\t\t\turl: `${this.config.apiBaseUrl}/accounts/${this.config.accountId}/vectorize/v2/indexes`,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\tbody: JSON.stringify({\n\t\t\t\tname: this.config.indexName,\n\t\t\t\tdescription: this.config.createIndex.description,\n\t\t\t\tconfig: {\n\t\t\t\t\tdimensions,\n\t\t\t\t\tmetric,\n\t\t\t\t},\n\t\t\t}),\n\t\t});\n\t}\n\n\tasync deleteIndex(): Promise<void> {\n\t\tawait requestCloudflare<unknown>({\n\t\t\turl: this.config.vectorizeBaseUrl,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\tmethod: \"DELETE\",\n\t\t});\n\t}\n\n\tasync upsert(vectors: VectorizeVector[]): Promise<string | undefined> {\n\t\tconst body = vectors.map((vector) => JSON.stringify(vector)).join(\"\\n\");\n\t\tconst result = await requestCloudflare<MutationResponse>({\n\t\t\turl: `${this.config.vectorizeBaseUrl}/upsert`,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/x-ndjson\",\n\t\t\t},\n\t\t\tbody,\n\t\t});\n\t\treturn result.mutationId;\n\t}\n\n\tasync query(params: {\n\t\tvector: number[];\n\t\tnamespace?: string;\n\t\ttopK?: number;\n\t\tfilter?: MetadataFilter;\n\t\treturnValues?: boolean;\n\t}): Promise<VectorizeQueryMatch[]> {\n\t\tconst result = await requestCloudflare<QueryResponse>({\n\t\t\turl: `${this.config.vectorizeBaseUrl}/query`,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\tbody: JSON.stringify({\n\t\t\t\tvector: params.vector,\n\t\t\t\ttopK: params.topK ?? this.config.topK,\n\t\t\t\tfilter: params.filter,\n\t\t\t\tnamespace: params.namespace,\n\t\t\t\treturnValues: params.returnValues ?? false,\n\t\t\t}),\n\t\t});\n\t\treturn result.matches ?? [];\n\t}\n\n\tasync getByIds(ids: string[]): Promise<VectorizeQueryMatch[]> {\n\t\tif (ids.length === 0) {\n\t\t\treturn [];\n\t\t}\n\t\treturn requestCloudflare<VectorizeQueryMatch[]>({\n\t\t\turl: `${this.config.vectorizeBaseUrl}/get_by_ids`,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\tbody: JSON.stringify({ ids }),\n\t\t});\n\t}\n\n\tasync deleteByIds(ids: string[]): Promise<string | undefined> {\n\t\tif (ids.length === 0) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst result = await requestCloudflare<MutationResponse>({\n\t\t\turl: `${this.config.vectorizeBaseUrl}/delete_by_ids`,\n\t\t\tapiToken: this.config.apiToken,\n\t\t\tbody: JSON.stringify({ ids }),\n\t\t});\n\t\treturn result.mutationId;\n\t}\n}\n"],"mappings":";;AAYA,IAAa,IAAb,MAA6B;CAC5B,YAAY,GAA+C;AAA9B,OAAA,SAAA;;CAE7B,MAAM,gBAAoD;AACzD,SAAO,EAA6C;GACnD,KAAK,KAAK,OAAO;GACjB,UAAU,KAAK,OAAO;GACtB,QAAQ;GACR,CAAC;;CAGH,MAAM,YAAY,GAAoB,IAAS,KAAK,OAAO,YAAY,QAA4C;AAClH,SAAO,EAA6C;GACnD,KAAK,GAAG,KAAK,OAAO,WAAW,YAAY,KAAK,OAAO,UAAU;GACjE,UAAU,KAAK,OAAO;GACtB,MAAM,KAAK,UAAU;IACpB,MAAM,KAAK,OAAO;IAClB,aAAa,KAAK,OAAO,YAAY;IACrC,QAAQ;KACP;KACA;KACA;IACD,CAAC;GACF,CAAC;;CAGH,MAAM,cAA6B;AAClC,QAAM,EAA2B;GAChC,KAAK,KAAK,OAAO;GACjB,UAAU,KAAK,OAAO;GACtB,QAAQ;GACR,CAAC;;CAGH,MAAM,OAAO,GAAyD;EACrE,IAAM,IAAO,EAAQ,KAAK,MAAW,KAAK,UAAU,EAAO,CAAC,CAAC,KAAK,KAAK;AASvE,UARe,MAAM,EAAoC;GACxD,KAAK,GAAG,KAAK,OAAO,iBAAiB;GACrC,UAAU,KAAK,OAAO;GACtB,SAAS,EACR,gBAAgB,wBAChB;GACD;GACA,CAAC,EACY;;CAGf,MAAM,MAAM,GAMuB;
|
|
1
|
+
{"version":3,"file":"vectorize-client.js","names":[],"sources":["../src/vectorize-client.ts"],"sourcesContent":["import { requestCloudflare } from \"./cloudflare-api.js\";\r\nimport type { MetadataFilter, ResolvedPluginConfig, VectorizeIndexDescription, VectorizeQueryMatch, VectorizeVector } from \"./types.js\";\r\n\r\ntype MutationResponse = {\r\n\tmutationId?: string;\r\n};\r\n\r\ntype QueryResponse = {\r\n\tcount?: number;\r\n\tmatches?: VectorizeQueryMatch[];\r\n};\r\n\r\nexport class VectorizeClient {\r\n\tconstructor(private readonly config: ResolvedPluginConfig) {}\r\n\r\n\tasync describeIndex(): Promise<VectorizeIndexDescription> {\r\n\t\treturn requestCloudflare<VectorizeIndexDescription>({\r\n\t\t\turl: this.config.vectorizeBaseUrl,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tmethod: \"GET\",\r\n\t\t});\r\n\t}\r\n\r\n\tasync createIndex(dimensions: number, metric = this.config.createIndex.metric): Promise<VectorizeIndexDescription> {\r\n\t\treturn requestCloudflare<VectorizeIndexDescription>({\r\n\t\t\turl: `${this.config.apiBaseUrl}/accounts/${this.config.accountId}/vectorize/v2/indexes`,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tname: this.config.indexName,\r\n\t\t\t\tdescription: this.config.createIndex.description,\r\n\t\t\t\tconfig: {\r\n\t\t\t\t\tdimensions,\r\n\t\t\t\t\tmetric,\r\n\t\t\t\t},\r\n\t\t\t}),\r\n\t\t});\r\n\t}\r\n\r\n\tasync deleteIndex(): Promise<void> {\r\n\t\tawait requestCloudflare<unknown>({\r\n\t\t\turl: this.config.vectorizeBaseUrl,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tmethod: \"DELETE\",\r\n\t\t});\r\n\t}\r\n\r\n\tasync upsert(vectors: VectorizeVector[]): Promise<string | undefined> {\r\n\t\tconst body = vectors.map((vector) => JSON.stringify(vector)).join(\"\\n\");\r\n\t\tconst result = await requestCloudflare<MutationResponse>({\r\n\t\t\turl: `${this.config.vectorizeBaseUrl}/upsert`,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\theaders: {\r\n\t\t\t\t\"Content-Type\": \"application/x-ndjson\",\r\n\t\t\t},\r\n\t\t\tbody,\r\n\t\t});\r\n\t\treturn result.mutationId;\r\n\t}\r\n\r\n\tasync query(params: {\r\n\t\tvector: number[];\r\n\t\tnamespace?: string;\r\n\t\ttopK?: number;\r\n\t\tfilter?: MetadataFilter;\r\n\t\treturnValues?: boolean;\r\n\t}): Promise<VectorizeQueryMatch[]> {\r\n\t\tconst result = await requestCloudflare<QueryResponse>({\r\n\t\t\turl: `${this.config.vectorizeBaseUrl}/query`,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tvector: params.vector,\r\n\t\t\t\ttopK: params.topK ?? this.config.topK,\r\n\t\t\t\tfilter: params.filter,\r\n\t\t\t\tnamespace: params.namespace,\r\n\t\t\t\treturnValues: params.returnValues ?? false,\r\n\t\t\t\treturnMetadata: \"all\",\r\n\t\t\t}),\r\n\t\t});\r\n\t\treturn result.matches ?? [];\r\n\t}\r\n\r\n\tasync getByIds(ids: string[]): Promise<VectorizeQueryMatch[]> {\r\n\t\tif (ids.length === 0) {\r\n\t\t\treturn [];\r\n\t\t}\r\n\t\treturn requestCloudflare<VectorizeQueryMatch[]>({\r\n\t\t\turl: `${this.config.vectorizeBaseUrl}/get_by_ids`,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tbody: JSON.stringify({ ids }),\r\n\t\t});\r\n\t}\r\n\r\n\tasync deleteByIds(ids: string[]): Promise<string | undefined> {\r\n\t\tif (ids.length === 0) {\r\n\t\t\treturn undefined;\r\n\t\t}\r\n\t\tconst result = await requestCloudflare<MutationResponse>({\r\n\t\t\turl: `${this.config.vectorizeBaseUrl}/delete_by_ids`,\r\n\t\t\tapiToken: this.config.apiToken,\r\n\t\t\tbody: JSON.stringify({ ids }),\r\n\t\t});\r\n\t\treturn result.mutationId;\r\n\t}\r\n}\r\n"],"mappings":";;AAYA,IAAa,IAAb,MAA6B;CAC5B,YAAY,GAA+C;AAA9B,OAAA,SAAA;;CAE7B,MAAM,gBAAoD;AACzD,SAAO,EAA6C;GACnD,KAAK,KAAK,OAAO;GACjB,UAAU,KAAK,OAAO;GACtB,QAAQ;GACR,CAAC;;CAGH,MAAM,YAAY,GAAoB,IAAS,KAAK,OAAO,YAAY,QAA4C;AAClH,SAAO,EAA6C;GACnD,KAAK,GAAG,KAAK,OAAO,WAAW,YAAY,KAAK,OAAO,UAAU;GACjE,UAAU,KAAK,OAAO;GACtB,MAAM,KAAK,UAAU;IACpB,MAAM,KAAK,OAAO;IAClB,aAAa,KAAK,OAAO,YAAY;IACrC,QAAQ;KACP;KACA;KACA;IACD,CAAC;GACF,CAAC;;CAGH,MAAM,cAA6B;AAClC,QAAM,EAA2B;GAChC,KAAK,KAAK,OAAO;GACjB,UAAU,KAAK,OAAO;GACtB,QAAQ;GACR,CAAC;;CAGH,MAAM,OAAO,GAAyD;EACrE,IAAM,IAAO,EAAQ,KAAK,MAAW,KAAK,UAAU,EAAO,CAAC,CAAC,KAAK,KAAK;AASvE,UARe,MAAM,EAAoC;GACxD,KAAK,GAAG,KAAK,OAAO,iBAAiB;GACrC,UAAU,KAAK,OAAO;GACtB,SAAS,EACR,gBAAgB,wBAChB;GACD;GACA,CAAC,EACY;;CAGf,MAAM,MAAM,GAMuB;AAalC,UAZe,MAAM,EAAiC;GACrD,KAAK,GAAG,KAAK,OAAO,iBAAiB;GACrC,UAAU,KAAK,OAAO;GACtB,MAAM,KAAK,UAAU;IACpB,QAAQ,EAAO;IACf,MAAM,EAAO,QAAQ,KAAK,OAAO;IACjC,QAAQ,EAAO;IACf,WAAW,EAAO;IAClB,cAAc,EAAO,gBAAgB;IACrC,gBAAgB;IAChB,CAAC;GACF,CAAC,EACY,WAAW,EAAE;;CAG5B,MAAM,SAAS,GAA+C;AAI7D,SAHI,EAAI,WAAW,IACX,EAAE,GAEH,EAAyC;GAC/C,KAAK,GAAG,KAAK,OAAO,iBAAiB;GACrC,UAAU,KAAK,OAAO;GACtB,MAAM,KAAK,UAAU,EAAE,QAAK,CAAC;GAC7B,CAAC;;CAGH,MAAM,YAAY,GAA4C;AACzD,QAAI,WAAW,EAQnB,SALe,MAAM,EAAoC;GACxD,KAAK,GAAG,KAAK,OAAO,iBAAiB;GACrC,UAAU,KAAK,OAAO;GACtB,MAAM,KAAK,UAAU,EAAE,QAAK,CAAC;GAC7B,CAAC,EACY"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-cloudflare-vectorize-memory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "OpenClaw memory plugin for Cloudflare Vectorize and Workers AI embeddings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,8 +60,9 @@
|
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "vite build && tsc -p tsconfig.build.json",
|
|
63
|
-
"test": "vitest run",
|
|
64
|
-
"test:
|
|
63
|
+
"test": "vitest run --exclude tests/live-integration.test.ts",
|
|
64
|
+
"test:integration": "npm run build && vitest run tests/live-integration.test.ts",
|
|
65
|
+
"test:watch": "vitest --exclude tests/live-integration.test.ts",
|
|
65
66
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
66
67
|
"publish:npmjs": "npm run check && npm test && npm run build && npm publish --access public"
|
|
67
68
|
}
|