@byted-las/contextlake-openclaw 1.0.8 → 1.0.11
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/dist/index.js +1 -1
- package/dist/src/commands/cli.d.ts +3 -0
- package/dist/src/commands/cli.js +75 -0
- package/dist/src/commands/index.js +52 -0
- package/dist/src/commands/slashcmd.d.ts +9 -0
- package/dist/src/commands/slashcmd.js +97 -0
- package/index.ts +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/commands/cli.ts +82 -0
- package/src/commands/index.ts +61 -1
- package/src/commands/slashcmd.ts +108 -0
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ const commands_1 = require("./src/commands");
|
|
|
4
4
|
const plugin = {
|
|
5
5
|
id: 'contextlake-openclaw',
|
|
6
6
|
name: 'ContextLake',
|
|
7
|
-
version: '1.0.
|
|
7
|
+
version: '1.0.11',
|
|
8
8
|
description: 'A lightweight knowledge base plugin for OpenClaw using LanceDB and TOS, with data profiling support',
|
|
9
9
|
configSchema: {
|
|
10
10
|
type: 'object',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ContextLakeConfig } from '../utils/config';
|
|
2
2
|
export declare function getCliCommands(pluginConfig: ContextLakeConfig, logger: any): {
|
|
3
|
+
listS3ObjectsAction: (url: string, options: any) => Promise<void>;
|
|
4
|
+
readS3ObjectAction: (url: string, options: any) => Promise<void>;
|
|
5
|
+
generatePresignedUrlAction: (url: string, options: any) => Promise<void>;
|
|
3
6
|
searchAction: (query: any, options: any) => Promise<void>;
|
|
4
7
|
listAction: (options: any) => Promise<void>;
|
|
5
8
|
deleteAction: (options: any) => Promise<void>;
|
package/dist/src/commands/cli.js
CHANGED
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getCliCommands = getCliCommands;
|
|
4
4
|
const retrieve_1 = require("../lib/actions/retrieve");
|
|
5
5
|
const manage_1 = require("../lib/actions/manage");
|
|
6
|
+
const las_tools_1 = require("../lib/actions/las-tools");
|
|
7
|
+
const s3_tools_1 = require("../lib/actions/s3-tools");
|
|
6
8
|
const credentials_1 = require("../utils/credentials");
|
|
7
9
|
function parseOptionalInt(value, fallback) {
|
|
8
10
|
const parsed = Number.parseInt(String(value), 10);
|
|
@@ -30,7 +32,80 @@ function parseMetadata(metadata) {
|
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
function getCliCommands(pluginConfig, logger) {
|
|
35
|
+
const lasTools = (0, las_tools_1.getLasTools)(pluginConfig, logger);
|
|
36
|
+
const lasActions = {};
|
|
37
|
+
for (const tool of lasTools) {
|
|
38
|
+
lasActions[`${tool.name}Action`] = async (dataStr) => {
|
|
39
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI ${tool.name} started`);
|
|
40
|
+
try {
|
|
41
|
+
let data = {};
|
|
42
|
+
try {
|
|
43
|
+
data = JSON.parse(dataStr);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
// if it's not valid JSON and the tool accepts a simple string/url, try to handle it.
|
|
47
|
+
// But LAS tools typically require a data object.
|
|
48
|
+
data = { url: dataStr };
|
|
49
|
+
}
|
|
50
|
+
let params = tool.name === 'las_bare_image_text_embedding'
|
|
51
|
+
? data
|
|
52
|
+
: { data };
|
|
53
|
+
const result = await tool.execute('cli', params);
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.log(JSON.stringify(result, null, 2));
|
|
56
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI ${tool.name} success`);
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
console.error('Error:', e.message);
|
|
60
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI ${tool.name} failed`, { error: e.message, stack: e.stack });
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
33
64
|
return {
|
|
65
|
+
...lasActions,
|
|
66
|
+
listS3ObjectsAction: async (url, options) => {
|
|
67
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI list-s3-objects started`, { url, options });
|
|
68
|
+
try {
|
|
69
|
+
const result = await (0, s3_tools_1.listS3Objects)({ url }, options.prefix || '', parseOptionalInt(options.maxKeys, 1000), options.continuationToken);
|
|
70
|
+
// eslint-disable-next-line no-console
|
|
71
|
+
console.log(JSON.stringify(result, null, 2));
|
|
72
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI list-s3-objects success`);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
console.error('Error:', e.message);
|
|
76
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI list-s3-objects failed`, { error: e.message, stack: e.stack });
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
readS3ObjectAction: async (url, options) => {
|
|
80
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI read-s3-object started`, { url, options });
|
|
81
|
+
try {
|
|
82
|
+
const parsedUrl = new URL(url);
|
|
83
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
84
|
+
const result = await (0, s3_tools_1.readS3Object)({ url }, key, options.maxBytes ? parseOptionalInt(options.maxBytes, 0) : undefined);
|
|
85
|
+
// eslint-disable-next-line no-console
|
|
86
|
+
console.log(result.toString('utf-8'));
|
|
87
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI read-s3-object success`);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
console.error('Error:', e.message);
|
|
91
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI read-s3-object failed`, { error: e.message, stack: e.stack });
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
generatePresignedUrlAction: async (url, options) => {
|
|
95
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI generate-presigned-url started`, { url, options });
|
|
96
|
+
try {
|
|
97
|
+
const parsedUrl = new URL(url);
|
|
98
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
99
|
+
const result = await (0, s3_tools_1.getPresignedUrl)({ url }, key, parseOptionalInt(options.expiresIn, 3600));
|
|
100
|
+
// eslint-disable-next-line no-console
|
|
101
|
+
console.log(result);
|
|
102
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI generate-presigned-url success`);
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
console.error('Error:', e.message);
|
|
106
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI generate-presigned-url failed`, { error: e.message, stack: e.stack });
|
|
107
|
+
}
|
|
108
|
+
},
|
|
34
109
|
searchAction: async (query, options) => {
|
|
35
110
|
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI search started`, { query, options });
|
|
36
111
|
try {
|
|
@@ -67,6 +67,30 @@ function registerAll(ctx, logger) {
|
|
|
67
67
|
contextlake.command('onboard')
|
|
68
68
|
.description('Configure credentials for ContextLake')
|
|
69
69
|
.action(commands.onboardAction);
|
|
70
|
+
// S3 Tools
|
|
71
|
+
contextlake.command('list-s3-objects <url>')
|
|
72
|
+
.description('List objects in an S3-compatible bucket or local directory')
|
|
73
|
+
.option('--prefix <string>', 'Prefix to list')
|
|
74
|
+
.option('--max-keys <number>', 'Maximum number of keys to return', '1000')
|
|
75
|
+
.option('--continuation-token <string>', 'Continuation token for pagination')
|
|
76
|
+
.action(commands.listS3ObjectsAction);
|
|
77
|
+
contextlake.command('read-s3-object <url>')
|
|
78
|
+
.description('Read the contents of an S3 object')
|
|
79
|
+
.option('--max-bytes <number>', 'Maximum number of bytes to read')
|
|
80
|
+
.action(commands.readS3ObjectAction);
|
|
81
|
+
contextlake.command('generate-presigned-url <url>')
|
|
82
|
+
.description('Generate a presigned HTTP URL for an S3/TOS object')
|
|
83
|
+
.option('--expires-in <number>', 'Expiration time in seconds', '3600')
|
|
84
|
+
.action(commands.generatePresignedUrlAction);
|
|
85
|
+
// LAS Tools
|
|
86
|
+
const tools = (0, tools_1.getAgentTools)(pluginConfig, logger);
|
|
87
|
+
const lasTools = tools.lasTools;
|
|
88
|
+
for (const tool of lasTools) {
|
|
89
|
+
const cmdName = tool.name.replace(/_/g, '-');
|
|
90
|
+
contextlake.command(`${cmdName} <data>`)
|
|
91
|
+
.description(tool.label)
|
|
92
|
+
.action(commands[`${tool.name}Action`]);
|
|
93
|
+
}
|
|
70
94
|
}, { commands: ['contextlake'] });
|
|
71
95
|
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI commands registered`);
|
|
72
96
|
}
|
|
@@ -105,6 +129,34 @@ function registerAll(ctx, logger) {
|
|
|
105
129
|
acceptsArgs: false,
|
|
106
130
|
handler: slashCommands.listDatasourceHandler
|
|
107
131
|
});
|
|
132
|
+
ctx.registerCommand({
|
|
133
|
+
name: 'contextlake-list-s3-objects',
|
|
134
|
+
description: 'List objects in an S3-compatible bucket or local directory',
|
|
135
|
+
acceptsArgs: true,
|
|
136
|
+
handler: slashCommands.listS3ObjectsHandler
|
|
137
|
+
});
|
|
138
|
+
ctx.registerCommand({
|
|
139
|
+
name: 'contextlake-read-s3-object',
|
|
140
|
+
description: 'Read the contents of an S3 object',
|
|
141
|
+
acceptsArgs: true,
|
|
142
|
+
handler: slashCommands.readS3ObjectHandler
|
|
143
|
+
});
|
|
144
|
+
ctx.registerCommand({
|
|
145
|
+
name: 'contextlake-generate-presigned-url',
|
|
146
|
+
description: 'Generate a presigned HTTP URL for an S3/TOS object',
|
|
147
|
+
acceptsArgs: true,
|
|
148
|
+
handler: slashCommands.generatePresignedUrlHandler
|
|
149
|
+
});
|
|
150
|
+
const tools = (0, tools_1.getAgentTools)(pluginConfig, logger);
|
|
151
|
+
for (const tool of tools.lasTools) {
|
|
152
|
+
const cmdName = tool.name.replace(/_/g, '-');
|
|
153
|
+
ctx.registerCommand({
|
|
154
|
+
name: `contextlake-${cmdName}`,
|
|
155
|
+
description: tool.label,
|
|
156
|
+
acceptsArgs: true,
|
|
157
|
+
handler: slashCommands[`${tool.name}Handler`]
|
|
158
|
+
});
|
|
159
|
+
}
|
|
108
160
|
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash commands registered`);
|
|
109
161
|
}
|
|
110
162
|
catch (error) {
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { ContextLakeConfig } from '../utils/config';
|
|
2
2
|
export declare function getSlashCommands(pluginConfig: ContextLakeConfig, logger: any): {
|
|
3
|
+
listS3ObjectsHandler: (commandCtx: any) => Promise<{
|
|
4
|
+
text: string;
|
|
5
|
+
}>;
|
|
6
|
+
readS3ObjectHandler: (commandCtx: any) => Promise<{
|
|
7
|
+
text: string;
|
|
8
|
+
}>;
|
|
9
|
+
generatePresignedUrlHandler: (commandCtx: any) => Promise<{
|
|
10
|
+
text: string;
|
|
11
|
+
}>;
|
|
3
12
|
listHandler: (commandCtx: any) => Promise<{
|
|
4
13
|
text: string;
|
|
5
14
|
}>;
|
|
@@ -4,8 +4,105 @@ exports.getSlashCommands = getSlashCommands;
|
|
|
4
4
|
const retrieve_1 = require("../lib/actions/retrieve");
|
|
5
5
|
const manage_1 = require("../lib/actions/manage");
|
|
6
6
|
const profiler_1 = require("../lib/actions/profiler");
|
|
7
|
+
const las_tools_1 = require("../lib/actions/las-tools");
|
|
8
|
+
const s3_tools_1 = require("../lib/actions/s3-tools");
|
|
9
|
+
function parseOptionalInt(value, fallback) {
|
|
10
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
11
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
12
|
+
}
|
|
7
13
|
function getSlashCommands(pluginConfig, logger) {
|
|
14
|
+
const lasTools = (0, las_tools_1.getLasTools)(pluginConfig, logger);
|
|
15
|
+
const lasHandlers = {};
|
|
16
|
+
for (const tool of lasTools) {
|
|
17
|
+
lasHandlers[`${tool.name}Handler`] = async (commandCtx) => {
|
|
18
|
+
const rawArgs = commandCtx.args || "";
|
|
19
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command ${tool.name} started`, { args: rawArgs });
|
|
20
|
+
try {
|
|
21
|
+
if (!rawArgs.trim()) {
|
|
22
|
+
return { text: `**Error:** Missing arguments. Please provide JSON data or URL.` };
|
|
23
|
+
}
|
|
24
|
+
let data = {};
|
|
25
|
+
try {
|
|
26
|
+
data = JSON.parse(rawArgs);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
data = { url: rawArgs.trim() };
|
|
30
|
+
}
|
|
31
|
+
let params = tool.name === 'las_bare_image_text_embedding'
|
|
32
|
+
? data
|
|
33
|
+
: { data };
|
|
34
|
+
const result = await tool.execute('slash', params);
|
|
35
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command ${tool.name} completed`);
|
|
36
|
+
return { text: `**${tool.label} Results:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\`` };
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
logger.error(`[ContextLake] Slash ${tool.name} failed`, { error: e.message });
|
|
40
|
+
return { text: `**Error executing ${tool.label}:** ${e.message}` };
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
8
44
|
return {
|
|
45
|
+
...lasHandlers,
|
|
46
|
+
listS3ObjectsHandler: async (commandCtx) => {
|
|
47
|
+
const rawArgs = commandCtx.args || "";
|
|
48
|
+
const args = rawArgs.split(' ').filter((arg) => arg.trim() !== '');
|
|
49
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command list-s3-objects started`, { args });
|
|
50
|
+
try {
|
|
51
|
+
if (args.length === 0) {
|
|
52
|
+
return { text: `**Error:** Missing URL. Usage: /contextlake-list-s3-objects <url> [prefix] [maxKeys]` };
|
|
53
|
+
}
|
|
54
|
+
const url = args[0];
|
|
55
|
+
const prefix = args.length > 1 ? args[1] : '';
|
|
56
|
+
const maxKeys = args.length > 2 ? parseOptionalInt(args[2], 1000) : 1000;
|
|
57
|
+
const result = await (0, s3_tools_1.listS3Objects)({ url }, prefix, maxKeys);
|
|
58
|
+
return { text: `**List S3 Objects Results:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\`` };
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
logger.error(`[ContextLake] Slash list-s3-objects failed`, { error: e.message });
|
|
62
|
+
return { text: `**Error executing list-s3-objects:** ${e.message}` };
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
readS3ObjectHandler: async (commandCtx) => {
|
|
66
|
+
const rawArgs = commandCtx.args || "";
|
|
67
|
+
const args = rawArgs.split(' ').filter((arg) => arg.trim() !== '');
|
|
68
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command read-s3-object started`, { args });
|
|
69
|
+
try {
|
|
70
|
+
if (args.length === 0) {
|
|
71
|
+
return { text: `**Error:** Missing URL. Usage: /contextlake-read-s3-object <url> [maxBytes]` };
|
|
72
|
+
}
|
|
73
|
+
const url = args[0];
|
|
74
|
+
const parsedUrl = new URL(url);
|
|
75
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
76
|
+
const maxBytes = args.length > 1 ? parseOptionalInt(args[1], 0) : undefined;
|
|
77
|
+
const result = await (0, s3_tools_1.readS3Object)({ url }, key, maxBytes);
|
|
78
|
+
// Return base64 or string based on content? Returning string for simplicity in slash commands.
|
|
79
|
+
return { text: `**Read S3 Object Results:**\n\`\`\`\n${result.toString('utf-8')}\n\`\`\`` };
|
|
80
|
+
}
|
|
81
|
+
catch (e) {
|
|
82
|
+
logger.error(`[ContextLake] Slash read-s3-object failed`, { error: e.message });
|
|
83
|
+
return { text: `**Error executing read-s3-object:** ${e.message}` };
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
generatePresignedUrlHandler: async (commandCtx) => {
|
|
87
|
+
const rawArgs = commandCtx.args || "";
|
|
88
|
+
const args = rawArgs.split(' ').filter((arg) => arg.trim() !== '');
|
|
89
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command generate-presigned-url started`, { args });
|
|
90
|
+
try {
|
|
91
|
+
if (args.length === 0) {
|
|
92
|
+
return { text: `**Error:** Missing URL. Usage: /contextlake-generate-presigned-url <url> [expiresIn]` };
|
|
93
|
+
}
|
|
94
|
+
const url = args[0];
|
|
95
|
+
const parsedUrl = new URL(url);
|
|
96
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
97
|
+
const expiresIn = args.length > 1 ? parseOptionalInt(args[1], 3600) : 3600;
|
|
98
|
+
const result = await (0, s3_tools_1.getPresignedUrl)({ url }, key, expiresIn);
|
|
99
|
+
return { text: `**Presigned URL:**\n${result}` };
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
logger.error(`[ContextLake] Slash generate-presigned-url failed`, { error: e.message });
|
|
103
|
+
return { text: `**Error executing generate-presigned-url:** ${e.message}` };
|
|
104
|
+
}
|
|
105
|
+
},
|
|
9
106
|
listHandler: async (commandCtx) => {
|
|
10
107
|
const rawArgs = commandCtx.args || "";
|
|
11
108
|
const args = rawArgs.split(' ').filter((arg) => arg.trim() !== '');
|
package/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { registerAll } from './src/commands';
|
|
|
5
5
|
const plugin = {
|
|
6
6
|
id: 'contextlake-openclaw',
|
|
7
7
|
name: 'ContextLake',
|
|
8
|
-
version: '1.0.
|
|
8
|
+
version: '1.0.11',
|
|
9
9
|
description: 'A lightweight knowledge base plugin for OpenClaw using LanceDB and TOS, with data profiling support',
|
|
10
10
|
configSchema: {
|
|
11
11
|
type: 'object',
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "contextlake-openclaw",
|
|
3
3
|
"name": "ContextLake",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.11",
|
|
5
5
|
"description": "A lightweight knowledge base plugin for OpenClaw using LanceDB and TOS, with data profiling support",
|
|
6
6
|
"skills": ["./src/skills"],
|
|
7
7
|
"configSchema": {
|
package/package.json
CHANGED
package/src/commands/cli.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { ingestSource } from '../lib/actions/ingest';
|
|
|
3
3
|
import { retrieveAssets } from '../lib/actions/retrieve';
|
|
4
4
|
import { listAssets, deleteAssets } from '../lib/actions/manage';
|
|
5
5
|
import { connectDataSource, ConnectParams } from '../lib/actions/profiler';
|
|
6
|
+
import { getLasTools } from '../lib/actions/las-tools';
|
|
7
|
+
import { listS3Objects, readS3Object, getPresignedUrl } from '../lib/actions/s3-tools';
|
|
6
8
|
import { loadCredentials, saveCredentials, promptForInput } from '../utils/credentials';
|
|
7
9
|
import { ContextLakeConfig } from '../utils/config';
|
|
8
10
|
|
|
@@ -36,7 +38,87 @@ function parseMetadata(metadata: any): Record<string, any> {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export function getCliCommands(pluginConfig: ContextLakeConfig, logger: any) {
|
|
41
|
+
const lasTools = getLasTools(pluginConfig, logger);
|
|
42
|
+
const lasActions: Record<string, any> = {};
|
|
43
|
+
|
|
44
|
+
for (const tool of lasTools) {
|
|
45
|
+
lasActions[`${tool.name}Action`] = async (dataStr: string) => {
|
|
46
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI ${tool.name} started`);
|
|
47
|
+
try {
|
|
48
|
+
let data = {};
|
|
49
|
+
try {
|
|
50
|
+
data = JSON.parse(dataStr);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// if it's not valid JSON and the tool accepts a simple string/url, try to handle it.
|
|
53
|
+
// But LAS tools typically require a data object.
|
|
54
|
+
data = { url: dataStr };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let params = tool.name === 'las_bare_image_text_embedding'
|
|
58
|
+
? data
|
|
59
|
+
: { data };
|
|
60
|
+
|
|
61
|
+
const result = await tool.execute('cli', params);
|
|
62
|
+
// eslint-disable-next-line no-console
|
|
63
|
+
console.log(JSON.stringify(result, null, 2));
|
|
64
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI ${tool.name} success`);
|
|
65
|
+
} catch (e: any) {
|
|
66
|
+
console.error('Error:', e.message);
|
|
67
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI ${tool.name} failed`, { error: e.message, stack: e.stack });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
39
72
|
return {
|
|
73
|
+
...lasActions,
|
|
74
|
+
|
|
75
|
+
listS3ObjectsAction: async (url: string, options: any) => {
|
|
76
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI list-s3-objects started`, { url, options });
|
|
77
|
+
try {
|
|
78
|
+
const result = await listS3Objects(
|
|
79
|
+
{ url },
|
|
80
|
+
options.prefix || '',
|
|
81
|
+
parseOptionalInt(options.maxKeys, 1000),
|
|
82
|
+
options.continuationToken
|
|
83
|
+
);
|
|
84
|
+
// eslint-disable-next-line no-console
|
|
85
|
+
console.log(JSON.stringify(result, null, 2));
|
|
86
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI list-s3-objects success`);
|
|
87
|
+
} catch (e: any) {
|
|
88
|
+
console.error('Error:', e.message);
|
|
89
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI list-s3-objects failed`, { error: e.message, stack: e.stack });
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
readS3ObjectAction: async (url: string, options: any) => {
|
|
94
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI read-s3-object started`, { url, options });
|
|
95
|
+
try {
|
|
96
|
+
const parsedUrl = new URL(url);
|
|
97
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
98
|
+
const result = await readS3Object({ url }, key, options.maxBytes ? parseOptionalInt(options.maxBytes, 0) : undefined);
|
|
99
|
+
// eslint-disable-next-line no-console
|
|
100
|
+
console.log(result.toString('utf-8'));
|
|
101
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI read-s3-object success`);
|
|
102
|
+
} catch (e: any) {
|
|
103
|
+
console.error('Error:', e.message);
|
|
104
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI read-s3-object failed`, { error: e.message, stack: e.stack });
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
generatePresignedUrlAction: async (url: string, options: any) => {
|
|
109
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI generate-presigned-url started`, { url, options });
|
|
110
|
+
try {
|
|
111
|
+
const parsedUrl = new URL(url);
|
|
112
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
113
|
+
const result = await getPresignedUrl({ url }, key, parseOptionalInt(options.expiresIn, 3600));
|
|
114
|
+
// eslint-disable-next-line no-console
|
|
115
|
+
console.log(result);
|
|
116
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI generate-presigned-url success`);
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
console.error('Error:', e.message);
|
|
119
|
+
logger.error(`[${new Date().toISOString()}] [ContextLake] CLI generate-presigned-url failed`, { error: e.message, stack: e.stack });
|
|
120
|
+
}
|
|
121
|
+
},
|
|
40
122
|
searchAction: async (query: any, options: any) => {
|
|
41
123
|
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI search started`, { query, options });
|
|
42
124
|
try {
|
package/src/commands/index.ts
CHANGED
|
@@ -57,7 +57,7 @@ export function registerAll(ctx: OpenClawPluginApi, logger: PluginLogger) {
|
|
|
57
57
|
const contextlake = program.command('contextlake')
|
|
58
58
|
.description('Manage ContextLake knowledge base');
|
|
59
59
|
|
|
60
|
-
const commands = getCliCommands(pluginConfig, logger)
|
|
60
|
+
const commands = getCliCommands(pluginConfig, logger) as Record<string, any>;
|
|
61
61
|
|
|
62
62
|
// Search
|
|
63
63
|
contextlake.command('search <query>')
|
|
@@ -85,6 +85,34 @@ export function registerAll(ctx: OpenClawPluginApi, logger: PluginLogger) {
|
|
|
85
85
|
.description('Configure credentials for ContextLake')
|
|
86
86
|
.action(commands.onboardAction);
|
|
87
87
|
|
|
88
|
+
// S3 Tools
|
|
89
|
+
contextlake.command('list-s3-objects <url>')
|
|
90
|
+
.description('List objects in an S3-compatible bucket or local directory')
|
|
91
|
+
.option('--prefix <string>', 'Prefix to list')
|
|
92
|
+
.option('--max-keys <number>', 'Maximum number of keys to return', '1000')
|
|
93
|
+
.option('--continuation-token <string>', 'Continuation token for pagination')
|
|
94
|
+
.action(commands.listS3ObjectsAction);
|
|
95
|
+
|
|
96
|
+
contextlake.command('read-s3-object <url>')
|
|
97
|
+
.description('Read the contents of an S3 object')
|
|
98
|
+
.option('--max-bytes <number>', 'Maximum number of bytes to read')
|
|
99
|
+
.action(commands.readS3ObjectAction);
|
|
100
|
+
|
|
101
|
+
contextlake.command('generate-presigned-url <url>')
|
|
102
|
+
.description('Generate a presigned HTTP URL for an S3/TOS object')
|
|
103
|
+
.option('--expires-in <number>', 'Expiration time in seconds', '3600')
|
|
104
|
+
.action(commands.generatePresignedUrlAction);
|
|
105
|
+
|
|
106
|
+
// LAS Tools
|
|
107
|
+
const tools = getAgentTools(pluginConfig, logger);
|
|
108
|
+
const lasTools = tools.lasTools;
|
|
109
|
+
for (const tool of lasTools) {
|
|
110
|
+
const cmdName = tool.name.replace(/_/g, '-');
|
|
111
|
+
contextlake.command(`${cmdName} <data>`)
|
|
112
|
+
.description(tool.label)
|
|
113
|
+
.action(commands[`${tool.name}Action`]);
|
|
114
|
+
}
|
|
115
|
+
|
|
88
116
|
}, { commands: ['contextlake'] });
|
|
89
117
|
logger.info(`[${new Date().toISOString()}] [ContextLake] CLI commands registered`);
|
|
90
118
|
} catch (error: any) {
|
|
@@ -129,6 +157,38 @@ export function registerAll(ctx: OpenClawPluginApi, logger: PluginLogger) {
|
|
|
129
157
|
handler: slashCommands.listDatasourceHandler
|
|
130
158
|
});
|
|
131
159
|
|
|
160
|
+
ctx.registerCommand({
|
|
161
|
+
name: 'contextlake-list-s3-objects',
|
|
162
|
+
description: 'List objects in an S3-compatible bucket or local directory',
|
|
163
|
+
acceptsArgs: true,
|
|
164
|
+
handler: (slashCommands as any).listS3ObjectsHandler
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
ctx.registerCommand({
|
|
168
|
+
name: 'contextlake-read-s3-object',
|
|
169
|
+
description: 'Read the contents of an S3 object',
|
|
170
|
+
acceptsArgs: true,
|
|
171
|
+
handler: (slashCommands as any).readS3ObjectHandler
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
ctx.registerCommand({
|
|
175
|
+
name: 'contextlake-generate-presigned-url',
|
|
176
|
+
description: 'Generate a presigned HTTP URL for an S3/TOS object',
|
|
177
|
+
acceptsArgs: true,
|
|
178
|
+
handler: (slashCommands as any).generatePresignedUrlHandler
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const tools = getAgentTools(pluginConfig, logger);
|
|
182
|
+
for (const tool of tools.lasTools) {
|
|
183
|
+
const cmdName = tool.name.replace(/_/g, '-');
|
|
184
|
+
ctx.registerCommand({
|
|
185
|
+
name: `contextlake-${cmdName}`,
|
|
186
|
+
description: tool.label,
|
|
187
|
+
acceptsArgs: true,
|
|
188
|
+
handler: (slashCommands as any)[`${tool.name}Handler`]
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
132
192
|
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash commands registered`);
|
|
133
193
|
} catch (error: any) {
|
|
134
194
|
logger.error(`[${new Date().toISOString()}] [ContextLake] Error registering Slash commands: ${error.message}${error.stack ? '\\n' + error.stack : ''}`);
|
package/src/commands/slashcmd.ts
CHANGED
|
@@ -2,13 +2,121 @@ import { ingestSource } from '../lib/actions/ingest';
|
|
|
2
2
|
import { retrieveAssets } from '../lib/actions/retrieve';
|
|
3
3
|
import { listAssets, deleteAssets } from '../lib/actions/manage';
|
|
4
4
|
import { connectDataSource, listDataSources, ConnectParams } from '../lib/actions/profiler';
|
|
5
|
+
import { getLasTools } from '../lib/actions/las-tools';
|
|
6
|
+
import { listS3Objects, readS3Object, getPresignedUrl } from '../lib/actions/s3-tools';
|
|
5
7
|
import { ContextLakeConfig } from '../utils/config';
|
|
6
8
|
import * as fs from 'fs';
|
|
7
9
|
import * as path from 'path';
|
|
8
10
|
import * as os from 'os';
|
|
9
11
|
|
|
12
|
+
function parseOptionalInt(value: any, fallback: number): number {
|
|
13
|
+
const parsed = Number.parseInt(String(value), 10);
|
|
14
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export function getSlashCommands(pluginConfig: ContextLakeConfig, logger: any) {
|
|
18
|
+
const lasTools = getLasTools(pluginConfig, logger);
|
|
19
|
+
const lasHandlers: Record<string, any> = {};
|
|
20
|
+
|
|
21
|
+
for (const tool of lasTools) {
|
|
22
|
+
lasHandlers[`${tool.name}Handler`] = async (commandCtx: any) => {
|
|
23
|
+
const rawArgs = commandCtx.args || "";
|
|
24
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command ${tool.name} started`, { args: rawArgs });
|
|
25
|
+
try {
|
|
26
|
+
if (!rawArgs.trim()) {
|
|
27
|
+
return { text: `**Error:** Missing arguments. Please provide JSON data or URL.` };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let data = {};
|
|
31
|
+
try {
|
|
32
|
+
data = JSON.parse(rawArgs);
|
|
33
|
+
} catch (e) {
|
|
34
|
+
data = { url: rawArgs.trim() };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let params = tool.name === 'las_bare_image_text_embedding'
|
|
38
|
+
? data
|
|
39
|
+
: { data };
|
|
40
|
+
|
|
41
|
+
const result = await tool.execute('slash', params);
|
|
42
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command ${tool.name} completed`);
|
|
43
|
+
return { text: `**${tool.label} Results:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\`` };
|
|
44
|
+
} catch (e: any) {
|
|
45
|
+
logger.error(`[ContextLake] Slash ${tool.name} failed`, { error: e.message });
|
|
46
|
+
return { text: `**Error executing ${tool.label}:** ${e.message}` };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
11
51
|
return {
|
|
52
|
+
...lasHandlers,
|
|
53
|
+
|
|
54
|
+
listS3ObjectsHandler: async (commandCtx: any) => {
|
|
55
|
+
const rawArgs = commandCtx.args || "";
|
|
56
|
+
const args = rawArgs.split(' ').filter((arg: string) => arg.trim() !== '');
|
|
57
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command list-s3-objects started`, { args });
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
if (args.length === 0) {
|
|
61
|
+
return { text: `**Error:** Missing URL. Usage: /contextlake-list-s3-objects <url> [prefix] [maxKeys]` };
|
|
62
|
+
}
|
|
63
|
+
const url = args[0];
|
|
64
|
+
const prefix = args.length > 1 ? args[1] : '';
|
|
65
|
+
const maxKeys = args.length > 2 ? parseOptionalInt(args[2], 1000) : 1000;
|
|
66
|
+
|
|
67
|
+
const result = await listS3Objects({ url }, prefix, maxKeys);
|
|
68
|
+
return { text: `**List S3 Objects Results:**\n\`\`\`json\n${JSON.stringify(result, null, 2)}\n\`\`\`` };
|
|
69
|
+
} catch (e: any) {
|
|
70
|
+
logger.error(`[ContextLake] Slash list-s3-objects failed`, { error: e.message });
|
|
71
|
+
return { text: `**Error executing list-s3-objects:** ${e.message}` };
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
readS3ObjectHandler: async (commandCtx: any) => {
|
|
76
|
+
const rawArgs = commandCtx.args || "";
|
|
77
|
+
const args = rawArgs.split(' ').filter((arg: string) => arg.trim() !== '');
|
|
78
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command read-s3-object started`, { args });
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
if (args.length === 0) {
|
|
82
|
+
return { text: `**Error:** Missing URL. Usage: /contextlake-read-s3-object <url> [maxBytes]` };
|
|
83
|
+
}
|
|
84
|
+
const url = args[0];
|
|
85
|
+
const parsedUrl = new URL(url);
|
|
86
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
87
|
+
const maxBytes = args.length > 1 ? parseOptionalInt(args[1], 0) : undefined;
|
|
88
|
+
|
|
89
|
+
const result = await readS3Object({ url }, key, maxBytes);
|
|
90
|
+
// Return base64 or string based on content? Returning string for simplicity in slash commands.
|
|
91
|
+
return { text: `**Read S3 Object Results:**\n\`\`\`\n${result.toString('utf-8')}\n\`\`\`` };
|
|
92
|
+
} catch (e: any) {
|
|
93
|
+
logger.error(`[ContextLake] Slash read-s3-object failed`, { error: e.message });
|
|
94
|
+
return { text: `**Error executing read-s3-object:** ${e.message}` };
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
generatePresignedUrlHandler: async (commandCtx: any) => {
|
|
99
|
+
const rawArgs = commandCtx.args || "";
|
|
100
|
+
const args = rawArgs.split(' ').filter((arg: string) => arg.trim() !== '');
|
|
101
|
+
logger.info(`[${new Date().toISOString()}] [ContextLake] Slash command generate-presigned-url started`, { args });
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
if (args.length === 0) {
|
|
105
|
+
return { text: `**Error:** Missing URL. Usage: /contextlake-generate-presigned-url <url> [expiresIn]` };
|
|
106
|
+
}
|
|
107
|
+
const url = args[0];
|
|
108
|
+
const parsedUrl = new URL(url);
|
|
109
|
+
const key = parsedUrl.pathname.replace(/^\//, '');
|
|
110
|
+
const expiresIn = args.length > 1 ? parseOptionalInt(args[1], 3600) : 3600;
|
|
111
|
+
|
|
112
|
+
const result = await getPresignedUrl({ url }, key, expiresIn);
|
|
113
|
+
return { text: `**Presigned URL:**\n${result}` };
|
|
114
|
+
} catch (e: any) {
|
|
115
|
+
logger.error(`[ContextLake] Slash generate-presigned-url failed`, { error: e.message });
|
|
116
|
+
return { text: `**Error executing generate-presigned-url:** ${e.message}` };
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
|
|
12
120
|
listHandler: async (commandCtx: any) => {
|
|
13
121
|
const rawArgs = commandCtx.args || "";
|
|
14
122
|
const args = rawArgs.split(' ').filter((arg: string) => arg.trim() !== '');
|