@hashgraphonline/standards-agent-kit 0.2.122 → 0.2.124
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/cjs/standards-agent-kit.cjs +1 -1
- package/dist/cjs/standards-agent-kit.cjs.map +1 -1
- package/dist/cjs/tools/inscriber/InscribeFromBufferTool.d.ts +6 -0
- package/dist/cjs/tools/inscriber/InscribeFromFileTool.d.ts +3 -0
- package/dist/cjs/tools/inscriber/InscribeFromUrlTool.d.ts +6 -0
- package/dist/cjs/tools/inscriber/InscribeHashinalTool.d.ts +6 -0
- package/dist/cjs/tools/inscriber/base-inscriber-tools.d.ts +15 -0
- package/dist/es/standards-agent-kit.es33.js +44 -0
- package/dist/es/standards-agent-kit.es33.js.map +1 -1
- package/dist/es/standards-agent-kit.es34.js +34 -6
- package/dist/es/standards-agent-kit.es34.js.map +1 -1
- package/dist/es/standards-agent-kit.es35.js +65 -14
- package/dist/es/standards-agent-kit.es35.js.map +1 -1
- package/dist/es/standards-agent-kit.es36.js +42 -6
- package/dist/es/standards-agent-kit.es36.js.map +1 -1
- package/dist/es/standards-agent-kit.es37.js +38 -6
- package/dist/es/standards-agent-kit.es37.js.map +1 -1
- package/dist/es/tools/inscriber/InscribeFromBufferTool.d.ts +6 -0
- package/dist/es/tools/inscriber/InscribeFromFileTool.d.ts +3 -0
- package/dist/es/tools/inscriber/InscribeFromUrlTool.d.ts +6 -0
- package/dist/es/tools/inscriber/InscribeHashinalTool.d.ts +6 -0
- package/dist/es/tools/inscriber/base-inscriber-tools.d.ts +15 -0
- package/dist/umd/standards-agent-kit.umd.js +1 -1
- package/dist/umd/standards-agent-kit.umd.js.map +1 -1
- package/dist/umd/tools/inscriber/InscribeFromBufferTool.d.ts +6 -0
- package/dist/umd/tools/inscriber/InscribeFromFileTool.d.ts +3 -0
- package/dist/umd/tools/inscriber/InscribeFromUrlTool.d.ts +6 -0
- package/dist/umd/tools/inscriber/InscribeHashinalTool.d.ts +6 -0
- package/dist/umd/tools/inscriber/base-inscriber-tools.d.ts +15 -0
- package/package.json +3 -2
- package/src/tools/inscriber/InscribeFromBufferTool.ts +49 -9
- package/src/tools/inscriber/InscribeFromFileTool.ts +88 -15
- package/src/tools/inscriber/InscribeFromUrlTool.ts +40 -11
- package/src/tools/inscriber/InscribeHashinalTool.ts +43 -6
- package/src/tools/inscriber/base-inscriber-tools.ts +87 -0
|
@@ -12,7 +12,9 @@ declare const inscribeFromBufferSchema: z.ZodObject<{
|
|
|
12
12
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
13
13
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
14
14
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
15
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
15
16
|
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
quoteOnly: boolean;
|
|
16
18
|
fileName: string;
|
|
17
19
|
base64Data: string;
|
|
18
20
|
tags?: string[] | undefined;
|
|
@@ -34,6 +36,7 @@ declare const inscribeFromBufferSchema: z.ZodObject<{
|
|
|
34
36
|
waitForConfirmation?: boolean | undefined;
|
|
35
37
|
timeoutMs?: number | undefined;
|
|
36
38
|
apiKey?: string | undefined;
|
|
39
|
+
quoteOnly?: boolean | undefined;
|
|
37
40
|
}>;
|
|
38
41
|
export declare class InscribeFromBufferTool extends BaseInscriberQueryTool<typeof inscribeFromBufferSchema> {
|
|
39
42
|
name: string;
|
|
@@ -50,7 +53,9 @@ export declare class InscribeFromBufferTool extends BaseInscriberQueryTool<typeo
|
|
|
50
53
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
51
54
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
52
55
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
56
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
53
57
|
}, "strip", z.ZodTypeAny, {
|
|
58
|
+
quoteOnly: boolean;
|
|
54
59
|
fileName: string;
|
|
55
60
|
base64Data: string;
|
|
56
61
|
tags?: string[] | undefined;
|
|
@@ -72,6 +77,7 @@ export declare class InscribeFromBufferTool extends BaseInscriberQueryTool<typeo
|
|
|
72
77
|
waitForConfirmation?: boolean | undefined;
|
|
73
78
|
timeoutMs?: number | undefined;
|
|
74
79
|
apiKey?: string | undefined;
|
|
80
|
+
quoteOnly?: boolean | undefined;
|
|
75
81
|
}>;
|
|
76
82
|
protected executeQuery(params: z.infer<typeof inscribeFromBufferSchema>, _runManager?: CallbackManagerForToolRun): Promise<unknown>;
|
|
77
83
|
private validateInput;
|
|
@@ -13,7 +13,9 @@ declare const inscribeFromFileSchema: z.ZodObject<{
|
|
|
13
13
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
14
14
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
15
15
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
16
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
16
17
|
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
quoteOnly: boolean;
|
|
17
19
|
filePath: string;
|
|
18
20
|
tags?: string[] | undefined;
|
|
19
21
|
metadata?: Record<string, unknown> | undefined;
|
|
@@ -31,6 +33,7 @@ declare const inscribeFromFileSchema: z.ZodObject<{
|
|
|
31
33
|
waitForConfirmation?: boolean | undefined;
|
|
32
34
|
timeoutMs?: number | undefined;
|
|
33
35
|
apiKey?: string | undefined;
|
|
36
|
+
quoteOnly?: boolean | undefined;
|
|
34
37
|
}>;
|
|
35
38
|
/**
|
|
36
39
|
* Tool for inscribing content from file
|
|
@@ -13,8 +13,10 @@ declare const inscribeFromUrlSchema: z.ZodObject<{
|
|
|
13
13
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
14
14
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
15
15
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
16
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
16
17
|
}, "strip", z.ZodTypeAny, {
|
|
17
18
|
url: string;
|
|
19
|
+
quoteOnly: boolean;
|
|
18
20
|
tags?: string[] | undefined;
|
|
19
21
|
metadata?: Record<string, unknown> | undefined;
|
|
20
22
|
mode?: "file" | "hashinal" | undefined;
|
|
@@ -31,6 +33,7 @@ declare const inscribeFromUrlSchema: z.ZodObject<{
|
|
|
31
33
|
waitForConfirmation?: boolean | undefined;
|
|
32
34
|
timeoutMs?: number | undefined;
|
|
33
35
|
apiKey?: string | undefined;
|
|
36
|
+
quoteOnly?: boolean | undefined;
|
|
34
37
|
}>;
|
|
35
38
|
/**
|
|
36
39
|
* Tool for inscribing content from URL
|
|
@@ -47,8 +50,10 @@ export declare class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof i
|
|
|
47
50
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
48
51
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
49
52
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
53
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
50
54
|
}, "strip", z.ZodTypeAny, {
|
|
51
55
|
url: string;
|
|
56
|
+
quoteOnly: boolean;
|
|
52
57
|
tags?: string[] | undefined;
|
|
53
58
|
metadata?: Record<string, unknown> | undefined;
|
|
54
59
|
mode?: "file" | "hashinal" | undefined;
|
|
@@ -65,6 +70,7 @@ export declare class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof i
|
|
|
65
70
|
waitForConfirmation?: boolean | undefined;
|
|
66
71
|
timeoutMs?: number | undefined;
|
|
67
72
|
apiKey?: string | undefined;
|
|
73
|
+
quoteOnly?: boolean | undefined;
|
|
68
74
|
}>;
|
|
69
75
|
protected executeQuery(params: z.infer<typeof inscribeFromUrlSchema>, _runManager?: CallbackManagerForToolRun): Promise<unknown>;
|
|
70
76
|
}
|
|
@@ -27,12 +27,14 @@ declare const inscribeHashinalSchema: z.ZodObject<{
|
|
|
27
27
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
28
28
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
29
29
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
30
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
30
31
|
}, "strip", z.ZodTypeAny, {
|
|
31
32
|
url: string;
|
|
32
33
|
type: string;
|
|
33
34
|
name: string;
|
|
34
35
|
description: string;
|
|
35
36
|
creator: string;
|
|
37
|
+
quoteOnly: boolean;
|
|
36
38
|
tags?: string[] | undefined;
|
|
37
39
|
properties?: Record<string, unknown> | undefined;
|
|
38
40
|
chunkSize?: number | undefined;
|
|
@@ -56,6 +58,7 @@ declare const inscribeHashinalSchema: z.ZodObject<{
|
|
|
56
58
|
waitForConfirmation?: boolean | undefined;
|
|
57
59
|
timeoutMs?: number | undefined;
|
|
58
60
|
apiKey?: string | undefined;
|
|
61
|
+
quoteOnly?: boolean | undefined;
|
|
59
62
|
attributes?: {
|
|
60
63
|
value: string | number;
|
|
61
64
|
trait_type: string;
|
|
@@ -91,12 +94,14 @@ export declare class InscribeHashinalTool extends BaseInscriberQueryTool<typeof
|
|
|
91
94
|
waitForConfirmation: z.ZodOptional<z.ZodBoolean>;
|
|
92
95
|
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
93
96
|
apiKey: z.ZodOptional<z.ZodString>;
|
|
97
|
+
quoteOnly: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
94
98
|
}, "strip", z.ZodTypeAny, {
|
|
95
99
|
url: string;
|
|
96
100
|
type: string;
|
|
97
101
|
name: string;
|
|
98
102
|
description: string;
|
|
99
103
|
creator: string;
|
|
104
|
+
quoteOnly: boolean;
|
|
100
105
|
tags?: string[] | undefined;
|
|
101
106
|
properties?: Record<string, unknown> | undefined;
|
|
102
107
|
chunkSize?: number | undefined;
|
|
@@ -120,6 +125,7 @@ export declare class InscribeHashinalTool extends BaseInscriberQueryTool<typeof
|
|
|
120
125
|
waitForConfirmation?: boolean | undefined;
|
|
121
126
|
timeoutMs?: number | undefined;
|
|
122
127
|
apiKey?: string | undefined;
|
|
128
|
+
quoteOnly?: boolean | undefined;
|
|
123
129
|
attributes?: {
|
|
124
130
|
value: string | number;
|
|
125
131
|
trait_type: string;
|
|
@@ -2,6 +2,7 @@ import { BaseHederaTransactionTool, BaseHederaQueryTool, BaseServiceBuilder } fr
|
|
|
2
2
|
import { InscriberBuilder } from '../../builders/inscriber/inscriber-builder';
|
|
3
3
|
import { InscriberTransactionToolParams, InscriberQueryToolParams } from './inscriber-tool-params';
|
|
4
4
|
import { ContentResolverInterface } from '../../types/content-resolver';
|
|
5
|
+
import { InscriptionInput, InscriptionOptions, QuoteResult } from '@hashgraphonline/standards-sdk';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
/**
|
|
7
8
|
* Base class for Inscriber transaction tools
|
|
@@ -19,6 +20,13 @@ export declare abstract class BaseInscriberTransactionTool<T extends z.ZodObject
|
|
|
19
20
|
* Get content resolver with fallback to registry
|
|
20
21
|
*/
|
|
21
22
|
protected getContentResolver(): ContentResolverInterface | null;
|
|
23
|
+
/**
|
|
24
|
+
* Generate a quote for an inscription without executing it
|
|
25
|
+
* @param input - The inscription input data
|
|
26
|
+
* @param options - Inscription options
|
|
27
|
+
* @returns Promise containing the quote result
|
|
28
|
+
*/
|
|
29
|
+
protected generateInscriptionQuote(input: InscriptionInput, options: InscriptionOptions): Promise<QuoteResult>;
|
|
22
30
|
}
|
|
23
31
|
/**
|
|
24
32
|
* Base class for Inscriber query tools
|
|
@@ -36,4 +44,11 @@ export declare abstract class BaseInscriberQueryTool<T extends z.ZodObject<z.Zod
|
|
|
36
44
|
* Get content resolver with fallback to registry
|
|
37
45
|
*/
|
|
38
46
|
protected getContentResolver(): ContentResolverInterface | null;
|
|
47
|
+
/**
|
|
48
|
+
* Generate a quote for an inscription without executing it
|
|
49
|
+
* @param input - The inscription input data
|
|
50
|
+
* @param options - Inscription options
|
|
51
|
+
* @returns Promise containing the quote result
|
|
52
|
+
*/
|
|
53
|
+
protected generateInscriptionQuote(input: InscriptionInput, options: InscriptionOptions): Promise<QuoteResult>;
|
|
39
54
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hashgraphonline/standards-agent-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.124",
|
|
4
4
|
"description": "A modular SDK for building on-chain autonomous agents using Hashgraph Online Standards, including HCS-10 for agent discovery and communication.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/standards-agent-kit.cjs",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"demo:plugin:weather": "tsx examples/plugins/weather/index.ts",
|
|
44
44
|
"demo:plugin:defi": "tsx examples/plugins/defi/index.ts",
|
|
45
45
|
"demo:plugin:openconvai": "tsx examples/openconvai-plugin-example.ts",
|
|
46
|
+
"demo:inscription-quotes": "tsx examples/inscription-quote-demo.ts",
|
|
46
47
|
"standards-agent:start": "tsx examples/standards-expert/cli.ts -- start",
|
|
47
48
|
"standards-agent:process-docs": "tsx examples/standards-expert/cli.ts -- process-docs --all-repos",
|
|
48
49
|
"typecheck": "tsc --noEmit"
|
|
@@ -89,7 +90,7 @@
|
|
|
89
90
|
},
|
|
90
91
|
"dependencies": {
|
|
91
92
|
"@hashgraph/sdk": "^2.69.0",
|
|
92
|
-
"@hashgraphonline/standards-sdk": "0.0.
|
|
93
|
+
"@hashgraphonline/standards-sdk": "^0.0.180",
|
|
93
94
|
"@langchain/community": "^0.3.49",
|
|
94
95
|
"@langchain/core": "^0.3.66",
|
|
95
96
|
"@langchain/openai": "^0.6.3",
|
|
@@ -47,6 +47,11 @@ const inscribeFromBufferSchema = z.object({
|
|
|
47
47
|
'Timeout in milliseconds for inscription (default: no timeout - waits until completion)'
|
|
48
48
|
),
|
|
49
49
|
apiKey: z.string().optional().describe('API key for inscription service'),
|
|
50
|
+
quoteOnly: z
|
|
51
|
+
.boolean()
|
|
52
|
+
.optional()
|
|
53
|
+
.default(false)
|
|
54
|
+
.describe('If true, returns a cost quote instead of executing the inscription'),
|
|
50
55
|
});
|
|
51
56
|
|
|
52
57
|
export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
@@ -54,7 +59,7 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
|
54
59
|
> {
|
|
55
60
|
name = 'inscribeFromBuffer';
|
|
56
61
|
description =
|
|
57
|
-
'Inscribe content that you have already retrieved or displayed. When user says "inscribe it" after you showed search results or other content, use THIS tool. The base64Data field accepts PLAIN TEXT (not just base64) and content reference IDs in format "content-ref:[id]". Pass the EXACT content from your previous response or MCP tool output. DO NOT generate new content or create repetitive text. Content references are automatically resolved to the original content for inscription.';
|
|
62
|
+
'Inscribe content that you have already retrieved or displayed. When user says "inscribe it" after you showed search results or other content, use THIS tool. The base64Data field accepts PLAIN TEXT (not just base64) and content reference IDs in format "content-ref:[id]". Pass the EXACT content from your previous response or MCP tool output. DO NOT generate new content or create repetitive text. Content references are automatically resolved to the original content for inscription. Set quoteOnly=true to get cost estimates without executing the inscription.';
|
|
58
63
|
|
|
59
64
|
private config = loadConfig();
|
|
60
65
|
|
|
@@ -85,7 +90,7 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
|
85
90
|
metadata: params.metadata,
|
|
86
91
|
tags: params.tags,
|
|
87
92
|
chunkSize: params.chunkSize,
|
|
88
|
-
waitForConfirmation: params.waitForConfirmation ?? true,
|
|
93
|
+
waitForConfirmation: params.quoteOnly ? false : (params.waitForConfirmation ?? true),
|
|
89
94
|
waitMaxAttempts: 10,
|
|
90
95
|
waitIntervalMs: 3000,
|
|
91
96
|
apiKey: params.apiKey,
|
|
@@ -94,8 +99,42 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
|
94
99
|
.includes('mainnet')
|
|
95
100
|
? 'mainnet'
|
|
96
101
|
: 'testnet',
|
|
102
|
+
quoteOnly: params.quoteOnly,
|
|
97
103
|
};
|
|
98
104
|
|
|
105
|
+
if (params.quoteOnly) {
|
|
106
|
+
try {
|
|
107
|
+
const quote = await this.generateInscriptionQuote(
|
|
108
|
+
{
|
|
109
|
+
type: 'buffer',
|
|
110
|
+
buffer,
|
|
111
|
+
fileName: resolvedFileName,
|
|
112
|
+
mimeType: resolvedMimeType,
|
|
113
|
+
},
|
|
114
|
+
options
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
success: true,
|
|
119
|
+
quote: {
|
|
120
|
+
totalCostHbar: quote.totalCostHbar,
|
|
121
|
+
validUntil: quote.validUntil,
|
|
122
|
+
breakdown: quote.breakdown,
|
|
123
|
+
},
|
|
124
|
+
contentInfo: {
|
|
125
|
+
fileName: resolvedFileName,
|
|
126
|
+
mimeType: resolvedMimeType,
|
|
127
|
+
sizeBytes: buffer.length,
|
|
128
|
+
},
|
|
129
|
+
message: `Quote generated for buffer content: ${resolvedFileName} (${(buffer.length / 1024).toFixed(2)} KB)\nTotal cost: ${quote.totalCostHbar} HBAR`,
|
|
130
|
+
};
|
|
131
|
+
} catch (error) {
|
|
132
|
+
const errorMessage =
|
|
133
|
+
error instanceof Error ? error.message : 'Failed to generate inscription quote';
|
|
134
|
+
throw new Error(`Quote generation failed: ${errorMessage}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
99
138
|
const timeoutMs =
|
|
100
139
|
params.timeoutMs || (options.waitForConfirmation ? 60000 : undefined);
|
|
101
140
|
|
|
@@ -210,20 +249,24 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
|
210
249
|
result: Awaited<ReturnType<typeof this.inscriberBuilder.inscribe>>,
|
|
211
250
|
options: InscriptionOptions
|
|
212
251
|
): string {
|
|
213
|
-
if (result.confirmed) {
|
|
214
|
-
const topicId = result.inscription?.topic_id || result.result.topicId;
|
|
252
|
+
if (result.confirmed && !result.quote) {
|
|
253
|
+
const topicId = result.inscription?.topic_id || (result.result as any).topicId;
|
|
215
254
|
const network = options.network || 'testnet';
|
|
216
255
|
const cdnUrl = topicId
|
|
217
256
|
? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}`
|
|
218
257
|
: null;
|
|
219
258
|
return `Successfully inscribed and confirmed content on the Hedera network!\n\nTransaction ID: ${
|
|
220
|
-
result.result.transactionId
|
|
259
|
+
(result.result as any).transactionId
|
|
221
260
|
}\nTopic ID: ${topicId || 'N/A'}${
|
|
222
261
|
cdnUrl ? `\nView inscription: ${cdnUrl}` : ''
|
|
223
262
|
}\n\nThe inscription is now available.`;
|
|
224
263
|
}
|
|
225
264
|
|
|
226
|
-
|
|
265
|
+
if (!result.quote && !result.confirmed) {
|
|
266
|
+
return `Successfully submitted inscription to the Hedera network!\n\nTransaction ID: ${(result.result as any).transactionId}\n\nThe inscription is processing and will be confirmed shortly.`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return 'Inscription operation completed.';
|
|
227
270
|
}
|
|
228
271
|
|
|
229
272
|
private async resolveContent(
|
|
@@ -238,11 +281,9 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
|
238
281
|
}> {
|
|
239
282
|
const trimmedInput = input.trim();
|
|
240
283
|
|
|
241
|
-
// Try to get resolver from either injected dependency or registry
|
|
242
284
|
const resolver = this.getContentResolver() || ContentResolverRegistry.getResolver();
|
|
243
285
|
|
|
244
286
|
if (!resolver) {
|
|
245
|
-
// No resolver available, handle content directly
|
|
246
287
|
return this.handleDirectContent(trimmedInput, providedMimeType, providedFileName);
|
|
247
288
|
}
|
|
248
289
|
|
|
@@ -267,7 +308,6 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<
|
|
|
267
308
|
}
|
|
268
309
|
}
|
|
269
310
|
|
|
270
|
-
// No reference found, handle as direct content
|
|
271
311
|
return this.handleDirectContent(trimmedInput, providedMimeType, providedFileName);
|
|
272
312
|
}
|
|
273
313
|
|
|
@@ -9,7 +9,12 @@ import * as path from 'path';
|
|
|
9
9
|
* Schema for inscribing from file
|
|
10
10
|
*/
|
|
11
11
|
const inscribeFromFileSchema = z.object({
|
|
12
|
-
filePath: z
|
|
12
|
+
filePath: z
|
|
13
|
+
.string()
|
|
14
|
+
.min(1, 'File path cannot be empty')
|
|
15
|
+
.describe(
|
|
16
|
+
'The file path of the content to inscribe. Must point to a valid, non-empty file.'
|
|
17
|
+
),
|
|
13
18
|
mode: z
|
|
14
19
|
.enum(['file', 'hashinal'])
|
|
15
20
|
.optional()
|
|
@@ -39,6 +44,13 @@ const inscribeFromFileSchema = z.object({
|
|
|
39
44
|
.optional()
|
|
40
45
|
.describe('Timeout in milliseconds for inscription (default: no timeout)'),
|
|
41
46
|
apiKey: z.string().optional().describe('API key for inscription service'),
|
|
47
|
+
quoteOnly: z
|
|
48
|
+
.boolean()
|
|
49
|
+
.optional()
|
|
50
|
+
.default(false)
|
|
51
|
+
.describe(
|
|
52
|
+
'If true, returns a cost quote instead of executing the inscription'
|
|
53
|
+
),
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
/**
|
|
@@ -49,7 +61,7 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
49
61
|
> {
|
|
50
62
|
name = 'inscribeFromFile';
|
|
51
63
|
description =
|
|
52
|
-
'Inscribe content from a local file to the Hedera network using a file path. IMPORTANT: Only use this tool when you have a valid file path to actual content. The file must exist and contain meaningful data (minimum 10 bytes). For files accessed through MCP filesystem tools, consider reading the file content first and using inscribeFromBuffer instead.';
|
|
64
|
+
'Inscribe content from a local file to the Hedera network using a file path. IMPORTANT: Only use this tool when you have a valid file path to actual content. The file must exist and contain meaningful data (minimum 10 bytes). For files accessed through MCP filesystem tools, consider reading the file content first and using inscribeFromBuffer instead. Set quoteOnly=true to get cost estimates without executing the inscription.';
|
|
53
65
|
|
|
54
66
|
get specificInputSchema(): typeof inscribeFromFileSchema {
|
|
55
67
|
return inscribeFromFileSchema;
|
|
@@ -59,9 +71,10 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
59
71
|
params: z.infer<typeof inscribeFromFileSchema>,
|
|
60
72
|
_runManager?: CallbackManagerForToolRun
|
|
61
73
|
): Promise<unknown> {
|
|
62
|
-
console.log(
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
console.log(
|
|
75
|
+
`[DEBUG] InscribeFromFileTool.executeQuery called with: ${params.filePath}`
|
|
76
|
+
);
|
|
77
|
+
|
|
65
78
|
let fileContent: Buffer;
|
|
66
79
|
try {
|
|
67
80
|
console.log(`[DEBUG] Checking file: ${params.filePath}`);
|
|
@@ -87,7 +100,12 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
if (stats.size > 100 * 1024 * 1024) {
|
|
90
|
-
console.log(
|
|
103
|
+
console.log(
|
|
104
|
+
`[InscribeFromFileTool] WARNING: Large file detected (${(
|
|
105
|
+
stats.size /
|
|
106
|
+
(1024 * 1024)
|
|
107
|
+
).toFixed(2)} MB)`
|
|
108
|
+
);
|
|
91
109
|
}
|
|
92
110
|
|
|
93
111
|
this.logger?.info('Reading file content...');
|
|
@@ -109,7 +127,11 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
109
127
|
const fileName = path.basename(params.filePath);
|
|
110
128
|
const mimeType = this.getMimeType(fileName);
|
|
111
129
|
if (mimeType.startsWith('text/') || mimeType === 'application/json') {
|
|
112
|
-
const textContent = fileContent.toString(
|
|
130
|
+
const textContent = fileContent.toString(
|
|
131
|
+
'utf8',
|
|
132
|
+
0,
|
|
133
|
+
Math.min(fileContent.length, 1000)
|
|
134
|
+
);
|
|
113
135
|
if (textContent.trim() === '') {
|
|
114
136
|
throw new Error(
|
|
115
137
|
`File "${params.filePath}" contains only whitespace or empty content. Cannot inscribe meaningless data.`
|
|
@@ -138,7 +160,9 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
138
160
|
metadata: params.metadata,
|
|
139
161
|
tags: params.tags,
|
|
140
162
|
chunkSize: params.chunkSize,
|
|
141
|
-
waitForConfirmation: params.
|
|
163
|
+
waitForConfirmation: params.quoteOnly
|
|
164
|
+
? false
|
|
165
|
+
: params.waitForConfirmation ?? true,
|
|
142
166
|
waitMaxAttempts: 10,
|
|
143
167
|
waitIntervalMs: 3000,
|
|
144
168
|
apiKey: params.apiKey,
|
|
@@ -147,15 +171,59 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
147
171
|
.includes('mainnet')
|
|
148
172
|
? 'mainnet'
|
|
149
173
|
: 'testnet',
|
|
174
|
+
quoteOnly: params.quoteOnly,
|
|
150
175
|
};
|
|
151
176
|
|
|
177
|
+
if (params.quoteOnly) {
|
|
178
|
+
try {
|
|
179
|
+
const quote = await this.generateInscriptionQuote(
|
|
180
|
+
{
|
|
181
|
+
type: 'buffer',
|
|
182
|
+
buffer: Buffer.from(base64Data, 'base64'),
|
|
183
|
+
fileName,
|
|
184
|
+
mimeType,
|
|
185
|
+
},
|
|
186
|
+
options
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
quote: {
|
|
192
|
+
totalCostHbar: quote.totalCostHbar,
|
|
193
|
+
validUntil: quote.validUntil,
|
|
194
|
+
breakdown: quote.breakdown,
|
|
195
|
+
},
|
|
196
|
+
contentInfo: {
|
|
197
|
+
fileName,
|
|
198
|
+
mimeType,
|
|
199
|
+
sizeBytes: fileContent.length,
|
|
200
|
+
filePath: params.filePath,
|
|
201
|
+
},
|
|
202
|
+
message: `Quote generated for file: ${fileName} (${(
|
|
203
|
+
fileContent.length / 1024
|
|
204
|
+
).toFixed(2)} KB)\nTotal cost: ${
|
|
205
|
+
quote.totalCostHbar
|
|
206
|
+
} HBAR`,
|
|
207
|
+
};
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const errorMessage =
|
|
210
|
+
error instanceof Error
|
|
211
|
+
? error.message
|
|
212
|
+
: 'Failed to generate inscription quote';
|
|
213
|
+
throw new Error(`Quote generation failed: ${errorMessage}`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
152
217
|
try {
|
|
153
|
-
let result:
|
|
154
|
-
|
|
218
|
+
let result: unknown;
|
|
219
|
+
|
|
155
220
|
if (params.timeoutMs) {
|
|
156
221
|
const timeoutPromise = new Promise((_, reject) => {
|
|
157
222
|
setTimeout(
|
|
158
|
-
() =>
|
|
223
|
+
() =>
|
|
224
|
+
reject(
|
|
225
|
+
new Error(`Inscription timed out after ${params.timeoutMs}ms`)
|
|
226
|
+
),
|
|
159
227
|
params.timeoutMs
|
|
160
228
|
);
|
|
161
229
|
});
|
|
@@ -184,19 +252,24 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
184
252
|
);
|
|
185
253
|
}
|
|
186
254
|
|
|
187
|
-
|
|
188
|
-
|
|
255
|
+
const inscriptionResult = result as any;
|
|
256
|
+
if (inscriptionResult.confirmed && !inscriptionResult.quote) {
|
|
257
|
+
const topicId =
|
|
258
|
+
inscriptionResult.inscription?.topic_id ||
|
|
259
|
+
inscriptionResult.result.topicId;
|
|
189
260
|
const network = options.network || 'testnet';
|
|
190
261
|
const cdnUrl = topicId
|
|
191
262
|
? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}`
|
|
192
263
|
: null;
|
|
193
264
|
return `Successfully inscribed and confirmed content on the Hedera network!\n\nTransaction ID: ${
|
|
194
|
-
|
|
265
|
+
inscriptionResult.result.transactionId
|
|
195
266
|
}\nTopic ID: ${topicId || 'N/A'}${
|
|
196
267
|
cdnUrl ? `\nView inscription: ${cdnUrl}` : ''
|
|
197
268
|
}\n\nThe inscription is now available.`;
|
|
269
|
+
} else if (!inscriptionResult.quote && !inscriptionResult.confirmed) {
|
|
270
|
+
return `Successfully submitted inscription to the Hedera network!\n\nTransaction ID: ${inscriptionResult.result.transactionId}\n\nThe inscription is processing and will be confirmed shortly.`;
|
|
198
271
|
} else {
|
|
199
|
-
return
|
|
272
|
+
return 'Inscription operation completed.';
|
|
200
273
|
}
|
|
201
274
|
} catch (error) {
|
|
202
275
|
const errorMessage =
|
|
@@ -40,6 +40,11 @@ const inscribeFromUrlSchema = z.object({
|
|
|
40
40
|
.string()
|
|
41
41
|
.optional()
|
|
42
42
|
.describe('API key for inscription service'),
|
|
43
|
+
quoteOnly: z
|
|
44
|
+
.boolean()
|
|
45
|
+
.optional()
|
|
46
|
+
.default(false)
|
|
47
|
+
.describe('If true, returns a cost quote instead of executing the inscription'),
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
|
|
@@ -48,7 +53,7 @@ const inscribeFromUrlSchema = z.object({
|
|
|
48
53
|
*/
|
|
49
54
|
export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeFromUrlSchema> {
|
|
50
55
|
name = 'inscribeFromUrl';
|
|
51
|
-
description = 'ONLY for direct FILE DOWNLOAD URLs ending with file extensions (.pdf, .jpg, .png, .json, .zip). NEVER use for web pages, articles, or ANY HTML content - it WILL FAIL. If you have already retrieved content from any source (including MCP tools), you MUST use inscribeFromBuffer instead. This tool downloads files from URLs - it does NOT inscribe content you already have. When asked to "inscribe it" after retrieving content, ALWAYS use inscribeFromBuffer with the actual content.';
|
|
56
|
+
description = 'ONLY for direct FILE DOWNLOAD URLs ending with file extensions (.pdf, .jpg, .png, .json, .zip). NEVER use for web pages, articles, or ANY HTML content - it WILL FAIL. If you have already retrieved content from any source (including MCP tools), you MUST use inscribeFromBuffer instead. This tool downloads files from URLs - it does NOT inscribe content you already have. When asked to "inscribe it" after retrieving content, ALWAYS use inscribeFromBuffer with the actual content. Set quoteOnly=true to get cost estimates without executing the inscription.';
|
|
52
57
|
|
|
53
58
|
get specificInputSchema() {
|
|
54
59
|
return inscribeFromUrlSchema;
|
|
@@ -86,7 +91,6 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
86
91
|
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
87
92
|
|
|
88
93
|
try {
|
|
89
|
-
// First try HEAD request
|
|
90
94
|
const headResponse = await fetch(params.url, {
|
|
91
95
|
method: 'HEAD',
|
|
92
96
|
signal: controller.signal,
|
|
@@ -101,7 +105,6 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
101
105
|
const contentType = headResponse.headers.get('content-type') || '';
|
|
102
106
|
const contentLength = headResponse.headers.get('content-length');
|
|
103
107
|
|
|
104
|
-
// Check if content type indicates HTML/web page
|
|
105
108
|
const webPageContentTypes = [
|
|
106
109
|
'text/html',
|
|
107
110
|
'application/xhtml+xml',
|
|
@@ -120,7 +123,6 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
120
123
|
throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
// If HEAD doesn't provide content-type, do a partial GET to check
|
|
124
126
|
if (!contentType || contentType === 'application/octet-stream') {
|
|
125
127
|
console.log(`[InscribeFromUrlTool] Content-Type unclear, fetching first 1KB to verify...`);
|
|
126
128
|
|
|
@@ -142,7 +144,6 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
142
144
|
const bytes = new Uint8Array(buffer);
|
|
143
145
|
const text = new TextDecoder('utf-8', { fatal: false }).decode(bytes.slice(0, 512));
|
|
144
146
|
|
|
145
|
-
// Check if it looks like HTML
|
|
146
147
|
if (text.toLowerCase().includes('<!doctype html') ||
|
|
147
148
|
text.toLowerCase().includes('<html') ||
|
|
148
149
|
text.match(/<meta\s+[^>]*>/i) ||
|
|
@@ -155,7 +156,6 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
155
156
|
if (getError instanceof Error && getError.message.includes('HTML content')) {
|
|
156
157
|
throw getError;
|
|
157
158
|
}
|
|
158
|
-
// If partial GET fails, continue anyway
|
|
159
159
|
console.log(`[InscribeFromUrlTool] Could not perform partial GET validation: ${getError instanceof Error ? getError.message : 'Unknown error'}`);
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -182,13 +182,40 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
182
182
|
metadata: params.metadata,
|
|
183
183
|
tags: params.tags,
|
|
184
184
|
chunkSize: params.chunkSize,
|
|
185
|
-
waitForConfirmation: params.waitForConfirmation ?? true,
|
|
185
|
+
waitForConfirmation: params.quoteOnly ? false : (params.waitForConfirmation ?? true),
|
|
186
186
|
waitMaxAttempts: 10,
|
|
187
187
|
waitIntervalMs: 3000,
|
|
188
188
|
apiKey: params.apiKey,
|
|
189
189
|
network: this.inscriberBuilder['hederaKit'].client.network.toString().includes('mainnet') ? 'mainnet' : 'testnet',
|
|
190
|
+
quoteOnly: params.quoteOnly,
|
|
190
191
|
};
|
|
191
192
|
|
|
193
|
+
if (params.quoteOnly) {
|
|
194
|
+
try {
|
|
195
|
+
const quote = await this.generateInscriptionQuote(
|
|
196
|
+
{ type: 'url', url: params.url },
|
|
197
|
+
options
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
success: true,
|
|
202
|
+
quote: {
|
|
203
|
+
totalCostHbar: quote.totalCostHbar,
|
|
204
|
+
validUntil: quote.validUntil,
|
|
205
|
+
breakdown: quote.breakdown,
|
|
206
|
+
},
|
|
207
|
+
contentInfo: {
|
|
208
|
+
url: params.url,
|
|
209
|
+
},
|
|
210
|
+
message: `Quote generated for URL: ${params.url}\nTotal cost: ${quote.totalCostHbar} HBAR`,
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
const errorMessage =
|
|
214
|
+
error instanceof Error ? error.message : 'Failed to generate inscription quote';
|
|
215
|
+
throw new Error(`Quote generation failed: ${errorMessage}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
192
219
|
try {
|
|
193
220
|
let result: Awaited<ReturnType<typeof this.inscriberBuilder.inscribe>>;
|
|
194
221
|
|
|
@@ -214,13 +241,15 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
214
241
|
);
|
|
215
242
|
}
|
|
216
243
|
|
|
217
|
-
if (result.confirmed) {
|
|
218
|
-
const topicId = result.inscription?.topic_id || result.result.topicId;
|
|
244
|
+
if (result.confirmed && !result.quote) {
|
|
245
|
+
const topicId = result.inscription?.topic_id || (result.result as any).topicId;
|
|
219
246
|
const network = options.network || 'testnet';
|
|
220
247
|
const cdnUrl = topicId ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}` : null;
|
|
221
|
-
return `Successfully inscribed and confirmed content on the Hedera network!\n\nTransaction ID: ${result.result.transactionId}\nTopic ID: ${topicId || 'N/A'}${cdnUrl ? `\nView inscription: ${cdnUrl}` : ''}\n\nThe inscription is now available.`;
|
|
248
|
+
return `Successfully inscribed and confirmed content on the Hedera network!\n\nTransaction ID: ${(result.result as any).transactionId}\nTopic ID: ${topicId || 'N/A'}${cdnUrl ? `\nView inscription: ${cdnUrl}` : ''}\n\nThe inscription is now available.`;
|
|
249
|
+
} else if (!result.quote && !result.confirmed) {
|
|
250
|
+
return `Successfully submitted inscription to the Hedera network!\n\nTransaction ID: ${(result.result as any).transactionId}\n\nThe inscription is processing and will be confirmed shortly.`;
|
|
222
251
|
} else {
|
|
223
|
-
return
|
|
252
|
+
return 'Inscription operation completed.';
|
|
224
253
|
}
|
|
225
254
|
} catch (error) {
|
|
226
255
|
const errorMessage = error instanceof Error ? error.message : 'Failed to inscribe from URL';
|