@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
|
}
|
|
@@ -18,6 +18,28 @@ class BaseInscriberTransactionTool extends BaseHederaTransactionTool {
|
|
|
18
18
|
getContentResolver() {
|
|
19
19
|
return this.contentResolver;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Generate a quote for an inscription without executing it
|
|
23
|
+
* @param input - The inscription input data
|
|
24
|
+
* @param options - Inscription options
|
|
25
|
+
* @returns Promise containing the quote result
|
|
26
|
+
*/
|
|
27
|
+
async generateInscriptionQuote(input, options) {
|
|
28
|
+
this.inscriberBuilder["hederaKit"].signer.getAccountId().toString();
|
|
29
|
+
this.inscriberBuilder["hederaKit"].signer?.getOperatorPrivateKey() ? this.inscriberBuilder["hederaKit"].signer.getOperatorPrivateKey().toStringRaw() : "";
|
|
30
|
+
const network = this.inscriberBuilder["hederaKit"].client.network;
|
|
31
|
+
const networkType = network.toString().includes("mainnet") ? "mainnet" : "testnet";
|
|
32
|
+
const quoteOptions = {
|
|
33
|
+
...options,
|
|
34
|
+
quoteOnly: true,
|
|
35
|
+
network: networkType
|
|
36
|
+
};
|
|
37
|
+
const result = await this.inscriberBuilder.inscribe(input, quoteOptions);
|
|
38
|
+
if (!result.quote || result.confirmed) {
|
|
39
|
+
throw new Error("Failed to generate quote - unexpected response type");
|
|
40
|
+
}
|
|
41
|
+
return result.result;
|
|
42
|
+
}
|
|
21
43
|
}
|
|
22
44
|
class BaseInscriberQueryTool extends BaseHederaQueryTool {
|
|
23
45
|
constructor(params) {
|
|
@@ -38,6 +60,28 @@ class BaseInscriberQueryTool extends BaseHederaQueryTool {
|
|
|
38
60
|
getContentResolver() {
|
|
39
61
|
return this.contentResolver;
|
|
40
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate a quote for an inscription without executing it
|
|
65
|
+
* @param input - The inscription input data
|
|
66
|
+
* @param options - Inscription options
|
|
67
|
+
* @returns Promise containing the quote result
|
|
68
|
+
*/
|
|
69
|
+
async generateInscriptionQuote(input, options) {
|
|
70
|
+
this.inscriberBuilder["hederaKit"].signer.getAccountId().toString();
|
|
71
|
+
this.inscriberBuilder["hederaKit"].signer?.getOperatorPrivateKey() ? this.inscriberBuilder["hederaKit"].signer.getOperatorPrivateKey().toStringRaw() : "";
|
|
72
|
+
const network = this.inscriberBuilder["hederaKit"].client.network;
|
|
73
|
+
const networkType = network.toString().includes("mainnet") ? "mainnet" : "testnet";
|
|
74
|
+
const quoteOptions = {
|
|
75
|
+
...options,
|
|
76
|
+
quoteOnly: true,
|
|
77
|
+
network: networkType
|
|
78
|
+
};
|
|
79
|
+
const result = await this.inscriberBuilder.inscribe(input, quoteOptions);
|
|
80
|
+
if (!result.quote || result.confirmed) {
|
|
81
|
+
throw new Error("Failed to generate quote - unexpected response type");
|
|
82
|
+
}
|
|
83
|
+
return result.result;
|
|
84
|
+
}
|
|
41
85
|
}
|
|
42
86
|
export {
|
|
43
87
|
BaseInscriberQueryTool,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-agent-kit.es33.js","sources":["../../src/tools/inscriber/base-inscriber-tools.ts"],"sourcesContent":["import {\n BaseHederaTransactionTool,\n BaseHederaQueryTool,\n BaseServiceBuilder,\n} from 'hedera-agent-kit';\nimport { InscriberBuilder } from '../../builders/inscriber/inscriber-builder';\nimport {\n InscriberTransactionToolParams,\n InscriberQueryToolParams,\n} from './inscriber-tool-params';\nimport type { ContentResolverInterface } from '../../types/content-resolver';\nimport { z } from 'zod';\n\n/**\n * Base class for Inscriber transaction tools\n */\nexport abstract class BaseInscriberTransactionTool<\n T extends z.ZodObject<\n z.ZodRawShape,\n z.UnknownKeysParam,\n z.ZodTypeAny\n > = z.ZodObject<z.ZodRawShape>\n> extends BaseHederaTransactionTool<T> {\n protected inscriberBuilder: InscriberBuilder;\n protected contentResolver: ContentResolverInterface | null;\n namespace = 'inscriber' as const;\n\n constructor(params: InscriberTransactionToolParams) {\n super(params);\n this.inscriberBuilder = params.inscriberBuilder;\n this.contentResolver = params.contentResolver || null;\n }\n\n /**\n * Override to return the InscriberBuilder\n */\n protected getServiceBuilder(): BaseServiceBuilder {\n return this.inscriberBuilder;\n }\n\n /**\n * Get content resolver with fallback to registry\n */\n protected getContentResolver(): ContentResolverInterface | null {\n return this.contentResolver;\n }\n}\n\n/**\n * Base class for Inscriber query tools\n */\nexport abstract class BaseInscriberQueryTool<\n T extends z.ZodObject<\n z.ZodRawShape,\n z.UnknownKeysParam,\n z.ZodTypeAny\n > = z.ZodObject<z.ZodRawShape>\n> extends BaseHederaQueryTool<T> {\n protected inscriberBuilder: InscriberBuilder;\n protected contentResolver: ContentResolverInterface | null;\n namespace = 'inscriber' as const;\n\n constructor(params: InscriberQueryToolParams) {\n super(params);\n this.inscriberBuilder = params.inscriberBuilder;\n this.contentResolver = params.contentResolver || null;\n }\n\n /**\n * Override to return the InscriberBuilder\n */\n protected getServiceBuilder(): BaseServiceBuilder {\n return this.inscriberBuilder;\n }\n\n /**\n * Get content resolver with fallback to registry\n */\n protected getContentResolver(): ContentResolverInterface | null {\n return this.contentResolver;\n }\n}"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"standards-agent-kit.es33.js","sources":["../../src/tools/inscriber/base-inscriber-tools.ts"],"sourcesContent":["import {\n BaseHederaTransactionTool,\n BaseHederaQueryTool,\n BaseServiceBuilder,\n} from 'hedera-agent-kit';\nimport { InscriberBuilder } from '../../builders/inscriber/inscriber-builder';\nimport {\n InscriberTransactionToolParams,\n InscriberQueryToolParams,\n} from './inscriber-tool-params';\nimport type { ContentResolverInterface } from '../../types/content-resolver';\nimport {\n InscriptionInput,\n InscriptionOptions,\n QuoteResult,\n} from '@hashgraphonline/standards-sdk';\nimport { z } from 'zod';\n\n/**\n * Base class for Inscriber transaction tools\n */\nexport abstract class BaseInscriberTransactionTool<\n T extends z.ZodObject<\n z.ZodRawShape,\n z.UnknownKeysParam,\n z.ZodTypeAny\n > = z.ZodObject<z.ZodRawShape>\n> extends BaseHederaTransactionTool<T> {\n protected inscriberBuilder: InscriberBuilder;\n protected contentResolver: ContentResolverInterface | null;\n namespace = 'inscriber' as const;\n\n constructor(params: InscriberTransactionToolParams) {\n super(params);\n this.inscriberBuilder = params.inscriberBuilder;\n this.contentResolver = params.contentResolver || null;\n }\n\n /**\n * Override to return the InscriberBuilder\n */\n protected getServiceBuilder(): BaseServiceBuilder {\n return this.inscriberBuilder;\n }\n\n /**\n * Get content resolver with fallback to registry\n */\n protected getContentResolver(): ContentResolverInterface | null {\n return this.contentResolver;\n }\n\n /**\n * Generate a quote for an inscription without executing it\n * @param input - The inscription input data\n * @param options - Inscription options\n * @returns Promise containing the quote result\n */\n protected async generateInscriptionQuote(\n input: InscriptionInput,\n options: InscriptionOptions\n ): Promise<QuoteResult> {\n const operatorId = this.inscriberBuilder['hederaKit'].signer.getAccountId().toString();\n const operatorPrivateKey = this.inscriberBuilder['hederaKit'].signer?.getOperatorPrivateKey()\n ? this.inscriberBuilder['hederaKit'].signer.getOperatorPrivateKey().toStringRaw()\n : '';\n\n const network = this.inscriberBuilder['hederaKit'].client.network;\n const networkType = network.toString().includes('mainnet')\n ? 'mainnet'\n : 'testnet';\n\n const clientConfig = {\n accountId: operatorId,\n privateKey: operatorPrivateKey,\n network: networkType as 'mainnet' | 'testnet',\n };\n\n const quoteOptions = {\n ...options,\n quoteOnly: true,\n network: networkType as 'mainnet' | 'testnet',\n };\n\n const result = await this.inscriberBuilder.inscribe(input, quoteOptions);\n \n if (!result.quote || result.confirmed) {\n throw new Error('Failed to generate quote - unexpected response type');\n }\n\n return result.result as QuoteResult;\n }\n}\n\n/**\n * Base class for Inscriber query tools\n */\nexport abstract class BaseInscriberQueryTool<\n T extends z.ZodObject<\n z.ZodRawShape,\n z.UnknownKeysParam,\n z.ZodTypeAny\n > = z.ZodObject<z.ZodRawShape>\n> extends BaseHederaQueryTool<T> {\n protected inscriberBuilder: InscriberBuilder;\n protected contentResolver: ContentResolverInterface | null;\n namespace = 'inscriber' as const;\n\n constructor(params: InscriberQueryToolParams) {\n super(params);\n this.inscriberBuilder = params.inscriberBuilder;\n this.contentResolver = params.contentResolver || null;\n }\n\n /**\n * Override to return the InscriberBuilder\n */\n protected getServiceBuilder(): BaseServiceBuilder {\n return this.inscriberBuilder;\n }\n\n /**\n * Get content resolver with fallback to registry\n */\n protected getContentResolver(): ContentResolverInterface | null {\n return this.contentResolver;\n }\n\n /**\n * Generate a quote for an inscription without executing it\n * @param input - The inscription input data\n * @param options - Inscription options\n * @returns Promise containing the quote result\n */\n protected async generateInscriptionQuote(\n input: InscriptionInput,\n options: InscriptionOptions\n ): Promise<QuoteResult> {\n const operatorId = this.inscriberBuilder['hederaKit'].signer.getAccountId().toString();\n const operatorPrivateKey = this.inscriberBuilder['hederaKit'].signer?.getOperatorPrivateKey()\n ? this.inscriberBuilder['hederaKit'].signer.getOperatorPrivateKey().toStringRaw()\n : '';\n\n const network = this.inscriberBuilder['hederaKit'].client.network;\n const networkType = network.toString().includes('mainnet')\n ? 'mainnet'\n : 'testnet';\n\n const clientConfig = {\n accountId: operatorId,\n privateKey: operatorPrivateKey,\n network: networkType as 'mainnet' | 'testnet',\n };\n\n const quoteOptions = {\n ...options,\n quoteOnly: true,\n network: networkType as 'mainnet' | 'testnet',\n };\n\n const result = await this.inscriberBuilder.inscribe(input, quoteOptions);\n \n if (!result.quote || result.confirmed) {\n throw new Error('Failed to generate quote - unexpected response type');\n }\n\n return result.result as QuoteResult;\n }\n}"],"names":[],"mappings":";AAqBO,MAAe,qCAMZ,0BAA6B;AAAA,EAKrC,YAAY,QAAwC;AAClD,UAAM,MAAM;AAHd,SAAA,YAAY;AAIV,SAAK,mBAAmB,OAAO;AAC/B,SAAK,kBAAkB,OAAO,mBAAmB;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKU,oBAAwC;AAChD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKU,qBAAsD;AAC9D,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAgB,yBACd,OACA,SACsB;AACH,SAAK,iBAAiB,WAAW,EAAE,OAAO,aAAA,EAAe,SAAA;AACjD,SAAK,iBAAiB,WAAW,EAAE,QAAQ,sBAAA,IAClE,KAAK,iBAAiB,WAAW,EAAE,OAAO,sBAAA,EAAwB,gBAClE;AAEJ,UAAM,UAAU,KAAK,iBAAiB,WAAW,EAAE,OAAO;AAC1D,UAAM,cAAc,QAAQ,SAAA,EAAW,SAAS,SAAS,IACrD,YACA;AAQJ,UAAM,eAAe;AAAA,MACnB,GAAG;AAAA,MACH,WAAW;AAAA,MACX,SAAS;AAAA,IAAA;AAGX,UAAM,SAAS,MAAM,KAAK,iBAAiB,SAAS,OAAO,YAAY;AAEvE,QAAI,CAAC,OAAO,SAAS,OAAO,WAAW;AACrC,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AAEA,WAAO,OAAO;AAAA,EAChB;AACF;AAKO,MAAe,+BAMZ,oBAAuB;AAAA,EAK/B,YAAY,QAAkC;AAC5C,UAAM,MAAM;AAHd,SAAA,YAAY;AAIV,SAAK,mBAAmB,OAAO;AAC/B,SAAK,kBAAkB,OAAO,mBAAmB;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA,EAKU,oBAAwC;AAChD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKU,qBAAsD;AAC9D,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAgB,yBACd,OACA,SACsB;AACH,SAAK,iBAAiB,WAAW,EAAE,OAAO,aAAA,EAAe,SAAA;AACjD,SAAK,iBAAiB,WAAW,EAAE,QAAQ,sBAAA,IAClE,KAAK,iBAAiB,WAAW,EAAE,OAAO,sBAAA,EAAwB,gBAClE;AAEJ,UAAM,UAAU,KAAK,iBAAiB,WAAW,EAAE,OAAO;AAC1D,UAAM,cAAc,QAAQ,SAAA,EAAW,SAAS,SAAS,IACrD,YACA;AAQJ,UAAM,eAAe;AAAA,MACnB,GAAG;AAAA,MACH,WAAW;AAAA,MACX,SAAS;AAAA,IAAA;AAGX,UAAM,SAAS,MAAM,KAAK,iBAAiB,SAAS,OAAO,YAAY;AAEvE,QAAI,CAAC,OAAO,SAAS,OAAO,WAAW;AACrC,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AAEA,WAAO,OAAO;AAAA,EAChB;AACF;"}
|
|
@@ -8,13 +8,14 @@ const inscribeFromUrlSchema = z.object({
|
|
|
8
8
|
chunkSize: z.number().int().positive().optional().describe("Chunk size for large files"),
|
|
9
9
|
waitForConfirmation: z.boolean().optional().describe("Whether to wait for inscription confirmation"),
|
|
10
10
|
timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds for inscription (default: no timeout - waits until completion)"),
|
|
11
|
-
apiKey: z.string().optional().describe("API key for inscription service")
|
|
11
|
+
apiKey: z.string().optional().describe("API key for inscription service"),
|
|
12
|
+
quoteOnly: z.boolean().optional().default(false).describe("If true, returns a cost quote instead of executing the inscription")
|
|
12
13
|
});
|
|
13
14
|
class InscribeFromUrlTool extends BaseInscriberQueryTool {
|
|
14
15
|
constructor() {
|
|
15
16
|
super(...arguments);
|
|
16
17
|
this.name = "inscribeFromUrl";
|
|
17
|
-
this.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.';
|
|
18
|
+
this.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.';
|
|
18
19
|
}
|
|
19
20
|
get specificInputSchema() {
|
|
20
21
|
return inscribeFromUrlSchema;
|
|
@@ -117,12 +118,37 @@ class InscribeFromUrlTool extends BaseInscriberQueryTool {
|
|
|
117
118
|
metadata: params.metadata,
|
|
118
119
|
tags: params.tags,
|
|
119
120
|
chunkSize: params.chunkSize,
|
|
120
|
-
waitForConfirmation: params.waitForConfirmation ?? true,
|
|
121
|
+
waitForConfirmation: params.quoteOnly ? false : params.waitForConfirmation ?? true,
|
|
121
122
|
waitMaxAttempts: 10,
|
|
122
123
|
waitIntervalMs: 3e3,
|
|
123
124
|
apiKey: params.apiKey,
|
|
124
|
-
network: this.inscriberBuilder["hederaKit"].client.network.toString().includes("mainnet") ? "mainnet" : "testnet"
|
|
125
|
+
network: this.inscriberBuilder["hederaKit"].client.network.toString().includes("mainnet") ? "mainnet" : "testnet",
|
|
126
|
+
quoteOnly: params.quoteOnly
|
|
125
127
|
};
|
|
128
|
+
if (params.quoteOnly) {
|
|
129
|
+
try {
|
|
130
|
+
const quote = await this.generateInscriptionQuote(
|
|
131
|
+
{ type: "url", url: params.url },
|
|
132
|
+
options
|
|
133
|
+
);
|
|
134
|
+
return {
|
|
135
|
+
success: true,
|
|
136
|
+
quote: {
|
|
137
|
+
totalCostHbar: quote.totalCostHbar,
|
|
138
|
+
validUntil: quote.validUntil,
|
|
139
|
+
breakdown: quote.breakdown
|
|
140
|
+
},
|
|
141
|
+
contentInfo: {
|
|
142
|
+
url: params.url
|
|
143
|
+
},
|
|
144
|
+
message: `Quote generated for URL: ${params.url}
|
|
145
|
+
Total cost: ${quote.totalCostHbar} HBAR`
|
|
146
|
+
};
|
|
147
|
+
} catch (error) {
|
|
148
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to generate inscription quote";
|
|
149
|
+
throw new Error(`Quote generation failed: ${errorMessage}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
126
152
|
try {
|
|
127
153
|
let result;
|
|
128
154
|
if (params.timeoutMs) {
|
|
@@ -145,7 +171,7 @@ class InscribeFromUrlTool extends BaseInscriberQueryTool {
|
|
|
145
171
|
options
|
|
146
172
|
);
|
|
147
173
|
}
|
|
148
|
-
if (result.confirmed) {
|
|
174
|
+
if (result.confirmed && !result.quote) {
|
|
149
175
|
const topicId = result.inscription?.topic_id || result.result.topicId;
|
|
150
176
|
const network = options.network || "testnet";
|
|
151
177
|
const cdnUrl = topicId ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}` : null;
|
|
@@ -156,12 +182,14 @@ Topic ID: ${topicId || "N/A"}${cdnUrl ? `
|
|
|
156
182
|
View inscription: ${cdnUrl}` : ""}
|
|
157
183
|
|
|
158
184
|
The inscription is now available.`;
|
|
159
|
-
} else {
|
|
185
|
+
} else if (!result.quote && !result.confirmed) {
|
|
160
186
|
return `Successfully submitted inscription to the Hedera network!
|
|
161
187
|
|
|
162
188
|
Transaction ID: ${result.result.transactionId}
|
|
163
189
|
|
|
164
190
|
The inscription is processing and will be confirmed shortly.`;
|
|
191
|
+
} else {
|
|
192
|
+
return "Inscription operation completed.";
|
|
165
193
|
}
|
|
166
194
|
} catch (error) {
|
|
167
195
|
const errorMessage = error instanceof Error ? error.message : "Failed to inscribe from URL";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-agent-kit.es34.js","sources":["../../src/tools/inscriber/InscribeFromUrlTool.ts"],"sourcesContent":["import { z } from 'zod';\nimport { BaseInscriberQueryTool } from './base-inscriber-tools';\nimport { InscriptionOptions } from '@hashgraphonline/standards-sdk';\nimport { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';\n\n/**\n * Schema for inscribing from URL\n */\nconst inscribeFromUrlSchema = z.object({\n url: z.string().url().describe('ONLY direct file download URLs with file extensions (.pdf, .jpg, .png, .json, .zip). NEVER use for web pages, articles, or when you already have content to inscribe.'),\n mode: z\n .enum(['file', 'hashinal'])\n .optional()\n .describe('Inscription mode: file or hashinal NFT'),\n metadata: z\n .record(z.unknown())\n .optional()\n .describe('Metadata to attach to the inscription'),\n tags: z\n .array(z.string())\n .optional()\n .describe('Tags to categorize the inscription'),\n chunkSize: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Chunk size for large files'),\n waitForConfirmation: z\n .boolean()\n .optional()\n .describe('Whether to wait for inscription confirmation'),\n timeoutMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Timeout in milliseconds for inscription (default: no timeout - waits until completion)'),\n apiKey: z\n .string()\n .optional()\n .describe('API key for inscription service'),\n});\n\n\n/**\n * Tool for inscribing content from URL\n */\nexport class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeFromUrlSchema> {\n name = 'inscribeFromUrl';\n 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.';\n\n get specificInputSchema() {\n return inscribeFromUrlSchema;\n }\n\n protected async executeQuery(\n params: z.infer<typeof inscribeFromUrlSchema>,\n _runManager?: CallbackManagerForToolRun\n ): Promise<unknown> {\n console.log(`[DEBUG] InscribeFromUrlTool.executeQuery called with URL: ${params.url}`);\n \n if (!params.url || params.url.trim() === '') {\n throw new Error('URL cannot be empty. Please provide a valid URL.');\n }\n\n try {\n const urlObj = new URL(params.url);\n if (!urlObj.protocol || !urlObj.host) {\n throw new Error('Invalid URL format. Please provide a complete URL with protocol (http/https).');\n }\n if (!['http:', 'https:'].includes(urlObj.protocol)) {\n throw new Error('Only HTTP and HTTPS URLs are supported for inscription.');\n }\n \n } catch (error) {\n if (error instanceof Error && error.message.includes('Cannot inscribe content from')) {\n throw error;\n }\n throw new Error(`Invalid URL: ${params.url}. Please provide a valid URL.`);\n }\n\n console.log(`[InscribeFromUrlTool] Validating URL content before inscription...`);\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n \n try {\n // First try HEAD request\n const headResponse = await fetch(params.url, {\n method: 'HEAD',\n signal: controller.signal,\n });\n \n clearTimeout(timeoutId);\n \n if (!headResponse.ok) {\n throw new Error(`URL returned error status ${headResponse.status}: ${headResponse.statusText}. Cannot inscribe content from inaccessible URLs.`);\n }\n\n const contentType = headResponse.headers.get('content-type') || '';\n const contentLength = headResponse.headers.get('content-length');\n \n // Check if content type indicates HTML/web page\n const webPageContentTypes = [\n 'text/html',\n 'application/xhtml+xml',\n 'text/xml'\n ];\n \n if (webPageContentTypes.some(type => contentType.toLowerCase().includes(type))) {\n throw new Error(`URL returns HTML/web page content (Content-Type: ${contentType}). This tool only works with direct file URLs (PDFs, images, JSON, etc.). For web page content, first retrieve the content using the appropriate MCP tool or web scraper, then use inscribeFromBuffer to inscribe it.`);\n }\n \n if (contentLength && parseInt(contentLength) === 0) {\n throw new Error('URL returns empty content (Content-Length: 0). Cannot inscribe empty content.');\n }\n\n if (contentLength && parseInt(contentLength) < 10) {\n throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);\n }\n \n // If HEAD doesn't provide content-type, do a partial GET to check\n if (!contentType || contentType === 'application/octet-stream') {\n console.log(`[InscribeFromUrlTool] Content-Type unclear, fetching first 1KB to verify...`);\n \n const getController = new AbortController();\n const getTimeoutId = setTimeout(() => getController.abort(), 5000);\n \n try {\n const getResponse = await fetch(params.url, {\n signal: getController.signal,\n headers: {\n 'Range': 'bytes=0-1023' // Get first 1KB\n }\n });\n \n clearTimeout(getTimeoutId);\n \n if (getResponse.ok || getResponse.status === 206) { // 206 is partial content\n const buffer = await getResponse.arrayBuffer();\n const bytes = new Uint8Array(buffer);\n const text = new TextDecoder('utf-8', { fatal: false }).decode(bytes.slice(0, 512));\n \n // Check if it looks like HTML\n if (text.toLowerCase().includes('<!doctype html') || \n text.toLowerCase().includes('<html') ||\n text.match(/<meta\\s+[^>]*>/i) ||\n text.match(/<title>/i)) {\n throw new Error(`URL returns HTML content. This tool only works with direct file URLs. For web page content, first retrieve it using the appropriate tool, then use inscribeFromBuffer.`);\n }\n }\n } catch (getError) {\n clearTimeout(getTimeoutId);\n if (getError instanceof Error && getError.message.includes('HTML content')) {\n throw getError;\n }\n // If partial GET fails, continue anyway\n console.log(`[InscribeFromUrlTool] Could not perform partial GET validation: ${getError instanceof Error ? getError.message : 'Unknown error'}`);\n }\n }\n\n console.log(`[InscribeFromUrlTool] URL validation passed. Content-Type: ${contentType}, Content-Length: ${contentLength || 'unknown'}`);\n } catch (fetchError) {\n clearTimeout(timeoutId);\n throw fetchError;\n }\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n console.log(`[InscribeFromUrlTool] Warning: URL validation timed out after 10 seconds. Proceeding with inscription attempt.`);\n } else if (error.message.includes('URL returned error') || error.message.includes('empty content') || error.message.includes('too small') || error.message.includes('HTML')) {\n throw error;\n } else {\n console.log(`[InscribeFromUrlTool] Warning: Could not validate URL with HEAD request: ${error.message}. Proceeding with inscription attempt.`);\n }\n }\n }\n\n const options: InscriptionOptions = {\n mode: params.mode,\n metadata: params.metadata,\n tags: params.tags,\n chunkSize: params.chunkSize,\n waitForConfirmation: params.waitForConfirmation ?? true,\n waitMaxAttempts: 10,\n waitIntervalMs: 3000,\n apiKey: params.apiKey,\n network: this.inscriberBuilder['hederaKit'].client.network.toString().includes('mainnet') ? 'mainnet' : 'testnet',\n };\n\n try {\n let result: Awaited<ReturnType<typeof this.inscriberBuilder.inscribe>>;\n \n if (params.timeoutMs) {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(\n () => reject(new Error(`Inscription timed out after ${params.timeoutMs}ms`)),\n params.timeoutMs\n );\n });\n\n result = await Promise.race([\n this.inscriberBuilder.inscribe(\n { type: 'url', url: params.url },\n options\n ),\n timeoutPromise\n ]);\n } else {\n result = await this.inscriberBuilder.inscribe(\n { type: 'url', url: params.url },\n options\n );\n }\n\n if (result.confirmed) {\n const topicId = result.inscription?.topic_id || result.result.topicId;\n const network = options.network || 'testnet';\n const cdnUrl = topicId ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}` : null;\n 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.`;\n } else {\n return `Successfully submitted inscription to the Hedera network!\\n\\nTransaction ID: ${result.result.transactionId}\\n\\nThe inscription is processing and will be confirmed shortly.`;\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Failed to inscribe from URL';\n throw new Error(`Inscription failed: ${errorMessage}`);\n }\n }\n}"],"names":[],"mappings":";;AAQA,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,KAAK,EAAE,OAAA,EAAS,IAAA,EAAM,SAAS,uKAAuK;AAAA,EACtM,MAAM,EACH,KAAK,CAAC,QAAQ,UAAU,CAAC,EACzB,SAAA,EACA,SAAS,wCAAwC;AAAA,EACpD,UAAU,EACP,OAAO,EAAE,QAAA,CAAS,EAClB,SAAA,EACA,SAAS,uCAAuC;AAAA,EACnD,MAAM,EACH,MAAM,EAAE,OAAA,CAAQ,EAChB,SAAA,EACA,SAAS,oCAAoC;AAAA,EAChD,WAAW,EACR,OAAA,EACA,IAAA,EACA,SAAA,EACA,SAAA,EACA,SAAS,4BAA4B;AAAA,EACxC,qBAAqB,EAClB,QAAA,EACA,SAAA,EACA,SAAS,8CAA8C;AAAA,EAC1D,WAAW,EACR,OAAA,EACA,IAAA,EACA,SAAA,EACA,SAAA,EACA,SAAS,wFAAwF;AAAA,EACpG,QAAQ,EACL,OAAA,EACA,SAAA,EACA,SAAS,iCAAiC;AAC/C,CAAC;AAMM,MAAM,4BAA4B,uBAAqD;AAAA,EAAvF,cAAA;AAAA,UAAA,GAAA,SAAA;AACL,SAAA,OAAO;AACP,SAAA,cAAc;AAAA,EAAA;AAAA,EAEd,IAAI,sBAAsB;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,MAAgB,aACd,QACA,aACkB;AAClB,YAAQ,IAAI,6DAA6D,OAAO,GAAG,EAAE;AAErF,QAAI,CAAC,OAAO,OAAO,OAAO,IAAI,KAAA,MAAW,IAAI;AAC3C,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAEA,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,OAAO,GAAG;AACjC,UAAI,CAAC,OAAO,YAAY,CAAC,OAAO,MAAM;AACpC,cAAM,IAAI,MAAM,+EAA+E;AAAA,MACjG;AACA,UAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,OAAO,QAAQ,GAAG;AAClD,cAAM,IAAI,MAAM,yDAAyD;AAAA,MAC3E;AAAA,IAEF,SAAS,OAAO;AACd,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,8BAA8B,GAAG;AACpF,cAAM;AAAA,MACR;AACA,YAAM,IAAI,MAAM,gBAAgB,OAAO,GAAG,+BAA+B;AAAA,IAC3E;AAEA,YAAQ,IAAI,oEAAoE;AAChF,QAAI;AACF,YAAM,aAAa,IAAI,gBAAA;AACvB,YAAM,YAAY,WAAW,MAAM,WAAW,MAAA,GAAS,GAAK;AAE5D,UAAI;AAEF,cAAM,eAAe,MAAM,MAAM,OAAO,KAAK;AAAA,UAC3C,QAAQ;AAAA,UACR,QAAQ,WAAW;AAAA,QAAA,CACpB;AAED,qBAAa,SAAS;AAEtB,YAAI,CAAC,aAAa,IAAI;AACpB,gBAAM,IAAI,MAAM,6BAA6B,aAAa,MAAM,KAAK,aAAa,UAAU,mDAAmD;AAAA,QACjJ;AAEA,cAAM,cAAc,aAAa,QAAQ,IAAI,cAAc,KAAK;AAChE,cAAM,gBAAgB,aAAa,QAAQ,IAAI,gBAAgB;AAG/D,cAAM,sBAAsB;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAGF,YAAI,oBAAoB,KAAK,CAAA,SAAQ,YAAY,cAAc,SAAS,IAAI,CAAC,GAAG;AAC9E,gBAAM,IAAI,MAAM,oDAAoD,WAAW,uNAAuN;AAAA,QACxS;AAEA,YAAI,iBAAiB,SAAS,aAAa,MAAM,GAAG;AAClD,gBAAM,IAAI,MAAM,+EAA+E;AAAA,QACjG;AAEA,YAAI,iBAAiB,SAAS,aAAa,IAAI,IAAI;AACjD,gBAAM,IAAI,MAAM,6BAA6B,aAAa,6CAA6C;AAAA,QACzG;AAGA,YAAI,CAAC,eAAe,gBAAgB,4BAA4B;AAC9D,kBAAQ,IAAI,6EAA6E;AAEzF,gBAAM,gBAAgB,IAAI,gBAAA;AAC1B,gBAAM,eAAe,WAAW,MAAM,cAAc,MAAA,GAAS,GAAI;AAEjE,cAAI;AACF,kBAAM,cAAc,MAAM,MAAM,OAAO,KAAK;AAAA,cAC1C,QAAQ,cAAc;AAAA,cACtB,SAAS;AAAA,gBACP,SAAS;AAAA;AAAA,cAAA;AAAA,YACX,CACD;AAED,yBAAa,YAAY;AAEzB,gBAAI,YAAY,MAAM,YAAY,WAAW,KAAK;AAChD,oBAAM,SAAS,MAAM,YAAY,YAAA;AACjC,oBAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,oBAAM,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC;AAGlF,kBAAI,KAAK,cAAc,SAAS,gBAAgB,KAC5C,KAAK,cAAc,SAAS,OAAO,KACnC,KAAK,MAAM,iBAAiB,KAC5B,KAAK,MAAM,UAAU,GAAG;AAC1B,sBAAM,IAAI,MAAM,wKAAwK;AAAA,cAC1L;AAAA,YACF;AAAA,UACF,SAAS,UAAU;AACjB,yBAAa,YAAY;AACzB,gBAAI,oBAAoB,SAAS,SAAS,QAAQ,SAAS,cAAc,GAAG;AAC1E,oBAAM;AAAA,YACR;AAEA,oBAAQ,IAAI,mEAAmE,oBAAoB,QAAQ,SAAS,UAAU,eAAe,EAAE;AAAA,UACjJ;AAAA,QACF;AAEA,gBAAQ,IAAI,8DAA8D,WAAW,qBAAqB,iBAAiB,SAAS,EAAE;AAAA,MACxI,SAAS,YAAY;AACnB,qBAAa,SAAS;AACtB,cAAM;AAAA,MACR;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,SAAS,cAAc;AAC/B,kBAAQ,IAAI,gHAAgH;AAAA,QAC9H,WAAW,MAAM,QAAQ,SAAS,oBAAoB,KAAK,MAAM,QAAQ,SAAS,eAAe,KAAK,MAAM,QAAQ,SAAS,WAAW,KAAK,MAAM,QAAQ,SAAS,MAAM,GAAG;AAC3K,gBAAM;AAAA,QACR,OAAO;AACL,kBAAQ,IAAI,4EAA4E,MAAM,OAAO,wCAAwC;AAAA,QAC/I;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAA8B;AAAA,MAClC,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,WAAW,OAAO;AAAA,MAClB,qBAAqB,OAAO,uBAAuB;AAAA,MACnD,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,SAAS,KAAK,iBAAiB,WAAW,EAAE,OAAO,QAAQ,WAAW,SAAS,SAAS,IAAI,YAAY;AAAA,IAAA;AAG1G,QAAI;AACF,UAAI;AAEJ,UAAI,OAAO,WAAW;AACpB,cAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD;AAAA,YACE,MAAM,OAAO,IAAI,MAAM,+BAA+B,OAAO,SAAS,IAAI,CAAC;AAAA,YAC3E,OAAO;AAAA,UAAA;AAAA,QAEX,CAAC;AAED,iBAAS,MAAM,QAAQ,KAAK;AAAA,UAC1B,KAAK,iBAAiB;AAAA,YACpB,EAAE,MAAM,OAAO,KAAK,OAAO,IAAA;AAAA,YAC3B;AAAA,UAAA;AAAA,UAEF;AAAA,QAAA,CACD;AAAA,MACH,OAAO;AACL,iBAAS,MAAM,KAAK,iBAAiB;AAAA,UACnC,EAAE,MAAM,OAAO,KAAK,OAAO,IAAA;AAAA,UAC3B;AAAA,QAAA;AAAA,MAEJ;AAEA,UAAI,OAAO,WAAW;AACpB,cAAM,UAAU,OAAO,aAAa,YAAY,OAAO,OAAO;AAC9D,cAAM,UAAU,QAAQ,WAAW;AACnC,cAAM,SAAS,UAAU,8CAA8C,OAAO,YAAY,OAAO,KAAK;AACtG,eAAO;AAAA;AAAA,kBAA0F,OAAO,OAAO,aAAa;AAAA,YAAe,WAAW,KAAK,GAAG,SAAS;AAAA,oBAAuB,MAAM,KAAK,EAAE;AAAA;AAAA;AAAA,MAC7M,OAAO;AACL,eAAO;AAAA;AAAA,kBAAgF,OAAO,OAAO,aAAa;AAAA;AAAA;AAAA,MACpH;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,YAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,IACvD;AAAA,EACF;AACF;"}
|
|
1
|
+
{"version":3,"file":"standards-agent-kit.es34.js","sources":["../../src/tools/inscriber/InscribeFromUrlTool.ts"],"sourcesContent":["import { z } from 'zod';\nimport { BaseInscriberQueryTool } from './base-inscriber-tools';\nimport { InscriptionOptions } from '@hashgraphonline/standards-sdk';\nimport { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';\n\n/**\n * Schema for inscribing from URL\n */\nconst inscribeFromUrlSchema = z.object({\n url: z.string().url().describe('ONLY direct file download URLs with file extensions (.pdf, .jpg, .png, .json, .zip). NEVER use for web pages, articles, or when you already have content to inscribe.'),\n mode: z\n .enum(['file', 'hashinal'])\n .optional()\n .describe('Inscription mode: file or hashinal NFT'),\n metadata: z\n .record(z.unknown())\n .optional()\n .describe('Metadata to attach to the inscription'),\n tags: z\n .array(z.string())\n .optional()\n .describe('Tags to categorize the inscription'),\n chunkSize: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Chunk size for large files'),\n waitForConfirmation: z\n .boolean()\n .optional()\n .describe('Whether to wait for inscription confirmation'),\n timeoutMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Timeout in milliseconds for inscription (default: no timeout - waits until completion)'),\n apiKey: z\n .string()\n .optional()\n .describe('API key for inscription service'),\n quoteOnly: z\n .boolean()\n .optional()\n .default(false)\n .describe('If true, returns a cost quote instead of executing the inscription'),\n});\n\n\n/**\n * Tool for inscribing content from URL\n */\nexport class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeFromUrlSchema> {\n name = 'inscribeFromUrl';\n 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.';\n\n get specificInputSchema() {\n return inscribeFromUrlSchema;\n }\n\n protected async executeQuery(\n params: z.infer<typeof inscribeFromUrlSchema>,\n _runManager?: CallbackManagerForToolRun\n ): Promise<unknown> {\n console.log(`[DEBUG] InscribeFromUrlTool.executeQuery called with URL: ${params.url}`);\n \n if (!params.url || params.url.trim() === '') {\n throw new Error('URL cannot be empty. Please provide a valid URL.');\n }\n\n try {\n const urlObj = new URL(params.url);\n if (!urlObj.protocol || !urlObj.host) {\n throw new Error('Invalid URL format. Please provide a complete URL with protocol (http/https).');\n }\n if (!['http:', 'https:'].includes(urlObj.protocol)) {\n throw new Error('Only HTTP and HTTPS URLs are supported for inscription.');\n }\n \n } catch (error) {\n if (error instanceof Error && error.message.includes('Cannot inscribe content from')) {\n throw error;\n }\n throw new Error(`Invalid URL: ${params.url}. Please provide a valid URL.`);\n }\n\n console.log(`[InscribeFromUrlTool] Validating URL content before inscription...`);\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 10000);\n \n try {\n const headResponse = await fetch(params.url, {\n method: 'HEAD',\n signal: controller.signal,\n });\n \n clearTimeout(timeoutId);\n \n if (!headResponse.ok) {\n throw new Error(`URL returned error status ${headResponse.status}: ${headResponse.statusText}. Cannot inscribe content from inaccessible URLs.`);\n }\n\n const contentType = headResponse.headers.get('content-type') || '';\n const contentLength = headResponse.headers.get('content-length');\n \n const webPageContentTypes = [\n 'text/html',\n 'application/xhtml+xml',\n 'text/xml'\n ];\n \n if (webPageContentTypes.some(type => contentType.toLowerCase().includes(type))) {\n throw new Error(`URL returns HTML/web page content (Content-Type: ${contentType}). This tool only works with direct file URLs (PDFs, images, JSON, etc.). For web page content, first retrieve the content using the appropriate MCP tool or web scraper, then use inscribeFromBuffer to inscribe it.`);\n }\n \n if (contentLength && parseInt(contentLength) === 0) {\n throw new Error('URL returns empty content (Content-Length: 0). Cannot inscribe empty content.');\n }\n\n if (contentLength && parseInt(contentLength) < 10) {\n throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);\n }\n \n if (!contentType || contentType === 'application/octet-stream') {\n console.log(`[InscribeFromUrlTool] Content-Type unclear, fetching first 1KB to verify...`);\n \n const getController = new AbortController();\n const getTimeoutId = setTimeout(() => getController.abort(), 5000);\n \n try {\n const getResponse = await fetch(params.url, {\n signal: getController.signal,\n headers: {\n 'Range': 'bytes=0-1023' // Get first 1KB\n }\n });\n \n clearTimeout(getTimeoutId);\n \n if (getResponse.ok || getResponse.status === 206) { // 206 is partial content\n const buffer = await getResponse.arrayBuffer();\n const bytes = new Uint8Array(buffer);\n const text = new TextDecoder('utf-8', { fatal: false }).decode(bytes.slice(0, 512));\n \n if (text.toLowerCase().includes('<!doctype html') || \n text.toLowerCase().includes('<html') ||\n text.match(/<meta\\s+[^>]*>/i) ||\n text.match(/<title>/i)) {\n throw new Error(`URL returns HTML content. This tool only works with direct file URLs. For web page content, first retrieve it using the appropriate tool, then use inscribeFromBuffer.`);\n }\n }\n } catch (getError) {\n clearTimeout(getTimeoutId);\n if (getError instanceof Error && getError.message.includes('HTML content')) {\n throw getError;\n }\n console.log(`[InscribeFromUrlTool] Could not perform partial GET validation: ${getError instanceof Error ? getError.message : 'Unknown error'}`);\n }\n }\n\n console.log(`[InscribeFromUrlTool] URL validation passed. Content-Type: ${contentType}, Content-Length: ${contentLength || 'unknown'}`);\n } catch (fetchError) {\n clearTimeout(timeoutId);\n throw fetchError;\n }\n } catch (error) {\n if (error instanceof Error) {\n if (error.name === 'AbortError') {\n console.log(`[InscribeFromUrlTool] Warning: URL validation timed out after 10 seconds. Proceeding with inscription attempt.`);\n } else if (error.message.includes('URL returned error') || error.message.includes('empty content') || error.message.includes('too small') || error.message.includes('HTML')) {\n throw error;\n } else {\n console.log(`[InscribeFromUrlTool] Warning: Could not validate URL with HEAD request: ${error.message}. Proceeding with inscription attempt.`);\n }\n }\n }\n\n const options: InscriptionOptions = {\n mode: params.mode,\n metadata: params.metadata,\n tags: params.tags,\n chunkSize: params.chunkSize,\n waitForConfirmation: params.quoteOnly ? false : (params.waitForConfirmation ?? true),\n waitMaxAttempts: 10,\n waitIntervalMs: 3000,\n apiKey: params.apiKey,\n network: this.inscriberBuilder['hederaKit'].client.network.toString().includes('mainnet') ? 'mainnet' : 'testnet',\n quoteOnly: params.quoteOnly,\n };\n\n if (params.quoteOnly) {\n try {\n const quote = await this.generateInscriptionQuote(\n { type: 'url', url: params.url },\n options\n );\n \n return {\n success: true,\n quote: {\n totalCostHbar: quote.totalCostHbar,\n validUntil: quote.validUntil,\n breakdown: quote.breakdown,\n },\n contentInfo: {\n url: params.url,\n },\n message: `Quote generated for URL: ${params.url}\\nTotal cost: ${quote.totalCostHbar} HBAR`,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Failed to generate inscription quote';\n throw new Error(`Quote generation failed: ${errorMessage}`);\n }\n }\n\n try {\n let result: Awaited<ReturnType<typeof this.inscriberBuilder.inscribe>>;\n \n if (params.timeoutMs) {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(\n () => reject(new Error(`Inscription timed out after ${params.timeoutMs}ms`)),\n params.timeoutMs\n );\n });\n\n result = await Promise.race([\n this.inscriberBuilder.inscribe(\n { type: 'url', url: params.url },\n options\n ),\n timeoutPromise\n ]);\n } else {\n result = await this.inscriberBuilder.inscribe(\n { type: 'url', url: params.url },\n options\n );\n }\n\n if (result.confirmed && !result.quote) {\n const topicId = result.inscription?.topic_id || (result.result as any).topicId;\n const network = options.network || 'testnet';\n const cdnUrl = topicId ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}` : null;\n 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.`;\n } else if (!result.quote && !result.confirmed) {\n 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.`;\n } else {\n return 'Inscription operation completed.';\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Failed to inscribe from URL';\n throw new Error(`Inscription failed: ${errorMessage}`);\n }\n }\n}"],"names":[],"mappings":";;AAQA,MAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,KAAK,EAAE,OAAA,EAAS,IAAA,EAAM,SAAS,uKAAuK;AAAA,EACtM,MAAM,EACH,KAAK,CAAC,QAAQ,UAAU,CAAC,EACzB,SAAA,EACA,SAAS,wCAAwC;AAAA,EACpD,UAAU,EACP,OAAO,EAAE,QAAA,CAAS,EAClB,SAAA,EACA,SAAS,uCAAuC;AAAA,EACnD,MAAM,EACH,MAAM,EAAE,OAAA,CAAQ,EAChB,SAAA,EACA,SAAS,oCAAoC;AAAA,EAChD,WAAW,EACR,OAAA,EACA,IAAA,EACA,SAAA,EACA,SAAA,EACA,SAAS,4BAA4B;AAAA,EACxC,qBAAqB,EAClB,QAAA,EACA,SAAA,EACA,SAAS,8CAA8C;AAAA,EAC1D,WAAW,EACR,OAAA,EACA,IAAA,EACA,SAAA,EACA,SAAA,EACA,SAAS,wFAAwF;AAAA,EACpG,QAAQ,EACL,OAAA,EACA,SAAA,EACA,SAAS,iCAAiC;AAAA,EAC7C,WAAW,EACR,UACA,SAAA,EACA,QAAQ,KAAK,EACb,SAAS,oEAAoE;AAClF,CAAC;AAMM,MAAM,4BAA4B,uBAAqD;AAAA,EAAvF,cAAA;AAAA,UAAA,GAAA,SAAA;AACL,SAAA,OAAO;AACP,SAAA,cAAc;AAAA,EAAA;AAAA,EAEd,IAAI,sBAAsB;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,MAAgB,aACd,QACA,aACkB;AAClB,YAAQ,IAAI,6DAA6D,OAAO,GAAG,EAAE;AAErF,QAAI,CAAC,OAAO,OAAO,OAAO,IAAI,KAAA,MAAW,IAAI;AAC3C,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAEA,QAAI;AACF,YAAM,SAAS,IAAI,IAAI,OAAO,GAAG;AACjC,UAAI,CAAC,OAAO,YAAY,CAAC,OAAO,MAAM;AACpC,cAAM,IAAI,MAAM,+EAA+E;AAAA,MACjG;AACA,UAAI,CAAC,CAAC,SAAS,QAAQ,EAAE,SAAS,OAAO,QAAQ,GAAG;AAClD,cAAM,IAAI,MAAM,yDAAyD;AAAA,MAC3E;AAAA,IAEF,SAAS,OAAO;AACd,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,8BAA8B,GAAG;AACpF,cAAM;AAAA,MACR;AACA,YAAM,IAAI,MAAM,gBAAgB,OAAO,GAAG,+BAA+B;AAAA,IAC3E;AAEA,YAAQ,IAAI,oEAAoE;AAChF,QAAI;AACF,YAAM,aAAa,IAAI,gBAAA;AACvB,YAAM,YAAY,WAAW,MAAM,WAAW,MAAA,GAAS,GAAK;AAE5D,UAAI;AACF,cAAM,eAAe,MAAM,MAAM,OAAO,KAAK;AAAA,UAC3C,QAAQ;AAAA,UACR,QAAQ,WAAW;AAAA,QAAA,CACpB;AAED,qBAAa,SAAS;AAEtB,YAAI,CAAC,aAAa,IAAI;AACpB,gBAAM,IAAI,MAAM,6BAA6B,aAAa,MAAM,KAAK,aAAa,UAAU,mDAAmD;AAAA,QACjJ;AAEA,cAAM,cAAc,aAAa,QAAQ,IAAI,cAAc,KAAK;AAChE,cAAM,gBAAgB,aAAa,QAAQ,IAAI,gBAAgB;AAE/D,cAAM,sBAAsB;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,QAAA;AAGF,YAAI,oBAAoB,KAAK,CAAA,SAAQ,YAAY,cAAc,SAAS,IAAI,CAAC,GAAG;AAC9E,gBAAM,IAAI,MAAM,oDAAoD,WAAW,uNAAuN;AAAA,QACxS;AAEA,YAAI,iBAAiB,SAAS,aAAa,MAAM,GAAG;AAClD,gBAAM,IAAI,MAAM,+EAA+E;AAAA,QACjG;AAEA,YAAI,iBAAiB,SAAS,aAAa,IAAI,IAAI;AACjD,gBAAM,IAAI,MAAM,6BAA6B,aAAa,6CAA6C;AAAA,QACzG;AAEA,YAAI,CAAC,eAAe,gBAAgB,4BAA4B;AAC9D,kBAAQ,IAAI,6EAA6E;AAEzF,gBAAM,gBAAgB,IAAI,gBAAA;AAC1B,gBAAM,eAAe,WAAW,MAAM,cAAc,MAAA,GAAS,GAAI;AAEjE,cAAI;AACF,kBAAM,cAAc,MAAM,MAAM,OAAO,KAAK;AAAA,cAC1C,QAAQ,cAAc;AAAA,cACtB,SAAS;AAAA,gBACP,SAAS;AAAA;AAAA,cAAA;AAAA,YACX,CACD;AAED,yBAAa,YAAY;AAEzB,gBAAI,YAAY,MAAM,YAAY,WAAW,KAAK;AAChD,oBAAM,SAAS,MAAM,YAAY,YAAA;AACjC,oBAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,oBAAM,OAAO,IAAI,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC;AAElF,kBAAI,KAAK,cAAc,SAAS,gBAAgB,KAC5C,KAAK,cAAc,SAAS,OAAO,KACnC,KAAK,MAAM,iBAAiB,KAC5B,KAAK,MAAM,UAAU,GAAG;AAC1B,sBAAM,IAAI,MAAM,wKAAwK;AAAA,cAC1L;AAAA,YACF;AAAA,UACF,SAAS,UAAU;AACjB,yBAAa,YAAY;AACzB,gBAAI,oBAAoB,SAAS,SAAS,QAAQ,SAAS,cAAc,GAAG;AAC1E,oBAAM;AAAA,YACR;AACA,oBAAQ,IAAI,mEAAmE,oBAAoB,QAAQ,SAAS,UAAU,eAAe,EAAE;AAAA,UACjJ;AAAA,QACF;AAEA,gBAAQ,IAAI,8DAA8D,WAAW,qBAAqB,iBAAiB,SAAS,EAAE;AAAA,MACxI,SAAS,YAAY;AACnB,qBAAa,SAAS;AACtB,cAAM;AAAA,MACR;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,SAAS,cAAc;AAC/B,kBAAQ,IAAI,gHAAgH;AAAA,QAC9H,WAAW,MAAM,QAAQ,SAAS,oBAAoB,KAAK,MAAM,QAAQ,SAAS,eAAe,KAAK,MAAM,QAAQ,SAAS,WAAW,KAAK,MAAM,QAAQ,SAAS,MAAM,GAAG;AAC3K,gBAAM;AAAA,QACR,OAAO;AACL,kBAAQ,IAAI,4EAA4E,MAAM,OAAO,wCAAwC;AAAA,QAC/I;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAA8B;AAAA,MAClC,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,WAAW,OAAO;AAAA,MAClB,qBAAqB,OAAO,YAAY,QAAS,OAAO,uBAAuB;AAAA,MAC/E,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,SAAS,KAAK,iBAAiB,WAAW,EAAE,OAAO,QAAQ,SAAA,EAAW,SAAS,SAAS,IAAI,YAAY;AAAA,MACxG,WAAW,OAAO;AAAA,IAAA;AAGpB,QAAI,OAAO,WAAW;AACpB,UAAI;AACF,cAAM,QAAQ,MAAM,KAAK;AAAA,UACvB,EAAE,MAAM,OAAO,KAAK,OAAO,IAAA;AAAA,UAC3B;AAAA,QAAA;AAGF,eAAO;AAAA,UACL,SAAS;AAAA,UACT,OAAO;AAAA,YACL,eAAe,MAAM;AAAA,YACrB,YAAY,MAAM;AAAA,YAClB,WAAW,MAAM;AAAA,UAAA;AAAA,UAEnB,aAAa;AAAA,YACX,KAAK,OAAO;AAAA,UAAA;AAAA,UAEd,SAAS,4BAA4B,OAAO,GAAG;AAAA,cAAiB,MAAM,aAAa;AAAA,QAAA;AAAA,MAEvF,SAAS,OAAO;AACd,cAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,cAAM,IAAI,MAAM,4BAA4B,YAAY,EAAE;AAAA,MAC5D;AAAA,IACF;AAEA,QAAI;AACF,UAAI;AAEJ,UAAI,OAAO,WAAW;AACpB,cAAM,iBAAiB,IAAI,QAAe,CAAC,GAAG,WAAW;AACvD;AAAA,YACE,MAAM,OAAO,IAAI,MAAM,+BAA+B,OAAO,SAAS,IAAI,CAAC;AAAA,YAC3E,OAAO;AAAA,UAAA;AAAA,QAEX,CAAC;AAED,iBAAS,MAAM,QAAQ,KAAK;AAAA,UAC1B,KAAK,iBAAiB;AAAA,YACpB,EAAE,MAAM,OAAO,KAAK,OAAO,IAAA;AAAA,YAC3B;AAAA,UAAA;AAAA,UAEF;AAAA,QAAA,CACD;AAAA,MACH,OAAO;AACL,iBAAS,MAAM,KAAK,iBAAiB;AAAA,UACnC,EAAE,MAAM,OAAO,KAAK,OAAO,IAAA;AAAA,UAC3B;AAAA,QAAA;AAAA,MAEJ;AAEA,UAAI,OAAO,aAAa,CAAC,OAAO,OAAO;AACrC,cAAM,UAAU,OAAO,aAAa,YAAa,OAAO,OAAe;AACvE,cAAM,UAAU,QAAQ,WAAW;AACnC,cAAM,SAAS,UAAU,8CAA8C,OAAO,YAAY,OAAO,KAAK;AACtG,eAAO;AAAA;AAAA,kBAA2F,OAAO,OAAe,aAAa;AAAA,YAAe,WAAW,KAAK,GAAG,SAAS;AAAA,oBAAuB,MAAM,KAAK,EAAE;AAAA;AAAA;AAAA,MACtN,WAAW,CAAC,OAAO,SAAS,CAAC,OAAO,WAAW;AAC7C,eAAO;AAAA;AAAA,kBAAiF,OAAO,OAAe,aAAa;AAAA;AAAA;AAAA,MAC7H,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,YAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,IACvD;AAAA,EACF;AACF;"}
|
|
@@ -3,26 +3,33 @@ import { BaseInscriberQueryTool } from "./standards-agent-kit.es33.js";
|
|
|
3
3
|
import * as fs from "fs/promises";
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
const inscribeFromFileSchema = z.object({
|
|
6
|
-
filePath: z.string().min(1, "File path cannot be empty").describe(
|
|
6
|
+
filePath: z.string().min(1, "File path cannot be empty").describe(
|
|
7
|
+
"The file path of the content to inscribe. Must point to a valid, non-empty file."
|
|
8
|
+
),
|
|
7
9
|
mode: z.enum(["file", "hashinal"]).optional().describe("Inscription mode: file or hashinal NFT"),
|
|
8
10
|
metadata: z.record(z.unknown()).optional().describe("Metadata to attach to the inscription"),
|
|
9
11
|
tags: z.array(z.string()).optional().describe("Tags to categorize the inscription"),
|
|
10
12
|
chunkSize: z.number().int().positive().optional().describe("Chunk size for large files"),
|
|
11
13
|
waitForConfirmation: z.boolean().optional().describe("Whether to wait for inscription confirmation"),
|
|
12
14
|
timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds for inscription (default: no timeout)"),
|
|
13
|
-
apiKey: z.string().optional().describe("API key for inscription service")
|
|
15
|
+
apiKey: z.string().optional().describe("API key for inscription service"),
|
|
16
|
+
quoteOnly: z.boolean().optional().default(false).describe(
|
|
17
|
+
"If true, returns a cost quote instead of executing the inscription"
|
|
18
|
+
)
|
|
14
19
|
});
|
|
15
20
|
class InscribeFromFileTool extends BaseInscriberQueryTool {
|
|
16
21
|
constructor() {
|
|
17
22
|
super(...arguments);
|
|
18
23
|
this.name = "inscribeFromFile";
|
|
19
|
-
this.description = "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.";
|
|
24
|
+
this.description = "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.";
|
|
20
25
|
}
|
|
21
26
|
get specificInputSchema() {
|
|
22
27
|
return inscribeFromFileSchema;
|
|
23
28
|
}
|
|
24
29
|
async executeQuery(params, _runManager) {
|
|
25
|
-
console.log(
|
|
30
|
+
console.log(
|
|
31
|
+
`[DEBUG] InscribeFromFileTool.executeQuery called with: ${params.filePath}`
|
|
32
|
+
);
|
|
26
33
|
let fileContent;
|
|
27
34
|
try {
|
|
28
35
|
console.log(`[DEBUG] Checking file: ${params.filePath}`);
|
|
@@ -43,7 +50,9 @@ class InscribeFromFileTool extends BaseInscriberQueryTool {
|
|
|
43
50
|
);
|
|
44
51
|
}
|
|
45
52
|
if (stats.size > 100 * 1024 * 1024) {
|
|
46
|
-
console.log(
|
|
53
|
+
console.log(
|
|
54
|
+
`[InscribeFromFileTool] WARNING: Large file detected (${(stats.size / (1024 * 1024)).toFixed(2)} MB)`
|
|
55
|
+
);
|
|
47
56
|
}
|
|
48
57
|
this.logger?.info("Reading file content...");
|
|
49
58
|
fileContent = await fs.readFile(params.filePath);
|
|
@@ -61,7 +70,11 @@ class InscribeFromFileTool extends BaseInscriberQueryTool {
|
|
|
61
70
|
const fileName2 = path.basename(params.filePath);
|
|
62
71
|
const mimeType2 = this.getMimeType(fileName2);
|
|
63
72
|
if (mimeType2.startsWith("text/") || mimeType2 === "application/json") {
|
|
64
|
-
const textContent = fileContent.toString(
|
|
73
|
+
const textContent = fileContent.toString(
|
|
74
|
+
"utf8",
|
|
75
|
+
0,
|
|
76
|
+
Math.min(fileContent.length, 1e3)
|
|
77
|
+
);
|
|
65
78
|
if (textContent.trim() === "") {
|
|
66
79
|
throw new Error(
|
|
67
80
|
`File "${params.filePath}" contains only whitespace or empty content. Cannot inscribe meaningless data.`
|
|
@@ -87,18 +100,53 @@ class InscribeFromFileTool extends BaseInscriberQueryTool {
|
|
|
87
100
|
metadata: params.metadata,
|
|
88
101
|
tags: params.tags,
|
|
89
102
|
chunkSize: params.chunkSize,
|
|
90
|
-
waitForConfirmation: params.waitForConfirmation ?? true,
|
|
103
|
+
waitForConfirmation: params.quoteOnly ? false : params.waitForConfirmation ?? true,
|
|
91
104
|
waitMaxAttempts: 10,
|
|
92
105
|
waitIntervalMs: 3e3,
|
|
93
106
|
apiKey: params.apiKey,
|
|
94
|
-
network: this.inscriberBuilder["hederaKit"].client.network.toString().includes("mainnet") ? "mainnet" : "testnet"
|
|
107
|
+
network: this.inscriberBuilder["hederaKit"].client.network.toString().includes("mainnet") ? "mainnet" : "testnet",
|
|
108
|
+
quoteOnly: params.quoteOnly
|
|
95
109
|
};
|
|
110
|
+
if (params.quoteOnly) {
|
|
111
|
+
try {
|
|
112
|
+
const quote = await this.generateInscriptionQuote(
|
|
113
|
+
{
|
|
114
|
+
type: "buffer",
|
|
115
|
+
buffer: Buffer.from(base64Data, "base64"),
|
|
116
|
+
fileName,
|
|
117
|
+
mimeType
|
|
118
|
+
},
|
|
119
|
+
options
|
|
120
|
+
);
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
quote: {
|
|
124
|
+
totalCostHbar: quote.totalCostHbar,
|
|
125
|
+
validUntil: quote.validUntil,
|
|
126
|
+
breakdown: quote.breakdown
|
|
127
|
+
},
|
|
128
|
+
contentInfo: {
|
|
129
|
+
fileName,
|
|
130
|
+
mimeType,
|
|
131
|
+
sizeBytes: fileContent.length,
|
|
132
|
+
filePath: params.filePath
|
|
133
|
+
},
|
|
134
|
+
message: `Quote generated for file: ${fileName} (${(fileContent.length / 1024).toFixed(2)} KB)
|
|
135
|
+
Total cost: ${quote.totalCostHbar} HBAR`
|
|
136
|
+
};
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to generate inscription quote";
|
|
139
|
+
throw new Error(`Quote generation failed: ${errorMessage}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
96
142
|
try {
|
|
97
143
|
let result;
|
|
98
144
|
if (params.timeoutMs) {
|
|
99
145
|
const timeoutPromise = new Promise((_, reject) => {
|
|
100
146
|
setTimeout(
|
|
101
|
-
() => reject(
|
|
147
|
+
() => reject(
|
|
148
|
+
new Error(`Inscription timed out after ${params.timeoutMs}ms`)
|
|
149
|
+
),
|
|
102
150
|
params.timeoutMs
|
|
103
151
|
);
|
|
104
152
|
});
|
|
@@ -125,23 +173,26 @@ class InscribeFromFileTool extends BaseInscriberQueryTool {
|
|
|
125
173
|
options
|
|
126
174
|
);
|
|
127
175
|
}
|
|
128
|
-
|
|
129
|
-
|
|
176
|
+
const inscriptionResult = result;
|
|
177
|
+
if (inscriptionResult.confirmed && !inscriptionResult.quote) {
|
|
178
|
+
const topicId = inscriptionResult.inscription?.topic_id || inscriptionResult.result.topicId;
|
|
130
179
|
const network = options.network || "testnet";
|
|
131
180
|
const cdnUrl = topicId ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}` : null;
|
|
132
181
|
return `Successfully inscribed and confirmed content on the Hedera network!
|
|
133
182
|
|
|
134
|
-
Transaction ID: ${
|
|
183
|
+
Transaction ID: ${inscriptionResult.result.transactionId}
|
|
135
184
|
Topic ID: ${topicId || "N/A"}${cdnUrl ? `
|
|
136
185
|
View inscription: ${cdnUrl}` : ""}
|
|
137
186
|
|
|
138
187
|
The inscription is now available.`;
|
|
139
|
-
} else {
|
|
188
|
+
} else if (!inscriptionResult.quote && !inscriptionResult.confirmed) {
|
|
140
189
|
return `Successfully submitted inscription to the Hedera network!
|
|
141
190
|
|
|
142
|
-
Transaction ID: ${
|
|
191
|
+
Transaction ID: ${inscriptionResult.result.transactionId}
|
|
143
192
|
|
|
144
193
|
The inscription is processing and will be confirmed shortly.`;
|
|
194
|
+
} else {
|
|
195
|
+
return "Inscription operation completed.";
|
|
145
196
|
}
|
|
146
197
|
} catch (error) {
|
|
147
198
|
const errorMessage = error instanceof Error ? error.message : "Failed to inscribe from file";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-agent-kit.es35.js","sources":["../../src/tools/inscriber/InscribeFromFileTool.ts"],"sourcesContent":["import { z } from 'zod';\nimport { BaseInscriberQueryTool } from './base-inscriber-tools';\nimport { InscriptionOptions } from '@hashgraphonline/standards-sdk';\nimport { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\n/**\n * Schema for inscribing from file\n */\nconst inscribeFromFileSchema = z.object({\n filePath: z.string().min(1, 'File path cannot be empty').describe('The file path of the content to inscribe. Must point to a valid, non-empty file.'),\n mode: z\n .enum(['file', 'hashinal'])\n .optional()\n .describe('Inscription mode: file or hashinal NFT'),\n metadata: z\n .record(z.unknown())\n .optional()\n .describe('Metadata to attach to the inscription'),\n tags: z\n .array(z.string())\n .optional()\n .describe('Tags to categorize the inscription'),\n chunkSize: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Chunk size for large files'),\n waitForConfirmation: z\n .boolean()\n .optional()\n .describe('Whether to wait for inscription confirmation'),\n timeoutMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Timeout in milliseconds for inscription (default: no timeout)'),\n apiKey: z.string().optional().describe('API key for inscription service'),\n});\n\n/**\n * Tool for inscribing content from file\n */\nexport class InscribeFromFileTool extends BaseInscriberQueryTool<\n typeof inscribeFromFileSchema\n> {\n name = 'inscribeFromFile';\n description =\n '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.';\n\n get specificInputSchema(): typeof inscribeFromFileSchema {\n return inscribeFromFileSchema;\n }\n\n protected async executeQuery(\n params: z.infer<typeof inscribeFromFileSchema>,\n _runManager?: CallbackManagerForToolRun\n ): Promise<unknown> {\n console.log(`[DEBUG] InscribeFromFileTool.executeQuery called with: ${params.filePath}`);\n \n // File validation\n let fileContent: Buffer;\n try {\n console.log(`[DEBUG] Checking file: ${params.filePath}`);\n console.log(`[DEBUG] Current working directory: ${process.cwd()}`);\n\n const stats = await fs.stat(params.filePath);\n if (!stats.isFile()) {\n throw new Error(`Path \"${params.filePath}\" is not a file`);\n }\n\n console.log(`[DEBUG] File size: ${stats.size} bytes`);\n\n if (stats.size === 0) {\n throw new Error(\n `File \"${params.filePath}\" is empty (0 bytes). Cannot inscribe empty files.`\n );\n }\n\n if (stats.size < 10) {\n throw new Error(\n `File \"${params.filePath}\" is too small (${stats.size} bytes). Files must contain at least 10 bytes of meaningful content.`\n );\n }\n\n if (stats.size > 100 * 1024 * 1024) {\n console.log(`[InscribeFromFileTool] WARNING: Large file detected (${(stats.size / (1024 * 1024)).toFixed(2)} MB)`);\n }\n\n this.logger?.info('Reading file content...');\n fileContent = await fs.readFile(params.filePath);\n this.logger?.info(`Read ${fileContent.length} bytes from file`);\n\n if (!fileContent || fileContent.length === 0) {\n throw new Error(\n `File \"${params.filePath}\" has no content after reading. Cannot inscribe empty files.`\n );\n }\n\n if (fileContent.length < 10) {\n throw new Error(\n `File \"${params.filePath}\" content is too small (${fileContent.length} bytes). Files must contain at least 10 bytes of meaningful content.`\n );\n }\n\n const fileName = path.basename(params.filePath);\n const mimeType = this.getMimeType(fileName);\n if (mimeType.startsWith('text/') || mimeType === 'application/json') {\n const textContent = fileContent.toString('utf8', 0, Math.min(fileContent.length, 1000));\n if (textContent.trim() === '') {\n throw new Error(\n `File \"${params.filePath}\" contains only whitespace or empty content. Cannot inscribe meaningless data.`\n );\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n throw new Error(`File not found: \"${params.filePath}\"`);\n }\n throw error;\n }\n throw new Error(`Failed to read file: ${error}`);\n }\n\n const base64Data = fileContent.toString('base64');\n this.logger?.info(`Converted to base64: ${base64Data.length} characters`);\n\n const fileName = path.basename(params.filePath);\n const mimeType = this.getMimeType(fileName);\n this.logger?.info(`File: ${fileName}, MIME type: ${mimeType}`);\n\n const options: InscriptionOptions = {\n mode: params.mode,\n metadata: params.metadata,\n tags: params.tags,\n chunkSize: params.chunkSize,\n waitForConfirmation: params.waitForConfirmation ?? true,\n waitMaxAttempts: 10,\n waitIntervalMs: 3000,\n apiKey: params.apiKey,\n network: this.inscriberBuilder['hederaKit'].client.network\n .toString()\n .includes('mainnet')\n ? 'mainnet'\n : 'testnet',\n };\n\n try {\n let result: any;\n \n if (params.timeoutMs) {\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(\n () => reject(new Error(`Inscription timed out after ${params.timeoutMs}ms`)),\n params.timeoutMs\n );\n });\n\n result = await Promise.race([\n this.inscriberBuilder.inscribe(\n {\n type: 'buffer',\n buffer: Buffer.from(base64Data, 'base64'),\n fileName,\n mimeType,\n },\n options\n ),\n timeoutPromise,\n ]);\n } else {\n result = await this.inscriberBuilder.inscribe(\n {\n type: 'buffer',\n buffer: Buffer.from(base64Data, 'base64'),\n fileName,\n mimeType,\n },\n options\n );\n }\n\n if (result.confirmed) {\n const topicId = result.inscription?.topic_id || result.result.topicId;\n const network = options.network || 'testnet';\n const cdnUrl = topicId\n ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}`\n : null;\n return `Successfully inscribed and confirmed content on the Hedera network!\\n\\nTransaction ID: ${\n result.result.transactionId\n }\\nTopic ID: ${topicId || 'N/A'}${\n cdnUrl ? `\\nView inscription: ${cdnUrl}` : ''\n }\\n\\nThe inscription is now available.`;\n } else {\n return `Successfully submitted inscription to the Hedera network!\\n\\nTransaction ID: ${result.result.transactionId}\\n\\nThe inscription is processing and will be confirmed shortly.`;\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Failed to inscribe from file';\n throw new Error(`Inscription failed: ${errorMessage}`);\n }\n }\n\n private getMimeType(fileName: string): string {\n const ext = path.extname(fileName).toLowerCase();\n const mimeTypes: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.svg': 'image/svg+xml',\n '.pdf': 'application/pdf',\n '.json': 'application/json',\n '.txt': 'text/plain',\n '.html': 'text/html',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.ts': 'application/typescript',\n '.mp4': 'video/mp4',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.zip': 'application/zip',\n };\n return mimeTypes[ext] || 'application/octet-stream';\n }\n}\n"],"names":["fileName","mimeType"],"mappings":";;;;AAUA,MAAM,yBAAyB,EAAE,OAAO;AAAA,EACtC,UAAU,EAAE,SAAS,IAAI,GAAG,2BAA2B,EAAE,SAAS,kFAAkF;AAAA,EACpJ,MAAM,EACH,KAAK,CAAC,QAAQ,UAAU,CAAC,EACzB,SAAA,EACA,SAAS,wCAAwC;AAAA,EACpD,UAAU,EACP,OAAO,EAAE,QAAA,CAAS,EAClB,SAAA,EACA,SAAS,uCAAuC;AAAA,EACnD,MAAM,EACH,MAAM,EAAE,OAAA,CAAQ,EAChB,SAAA,EACA,SAAS,oCAAoC;AAAA,EAChD,WAAW,EACR,OAAA,EACA,IAAA,EACA,SAAA,EACA,SAAA,EACA,SAAS,4BAA4B;AAAA,EACxC,qBAAqB,EAClB,QAAA,EACA,SAAA,EACA,SAAS,8CAA8C;AAAA,EAC1D,WAAW,EACR,OAAA,EACA,IAAA,EACA,SAAA,EACA,SAAA,EACA,SAAS,+DAA+D;AAAA,EAC3E,QAAQ,EAAE,OAAA,EAAS,SAAA,EAAW,SAAS,iCAAiC;AAC1E,CAAC;AAKM,MAAM,6BAA6B,uBAExC;AAAA,EAFK,cAAA;AAAA,UAAA,GAAA,SAAA;AAGL,SAAA,OAAO;AACP,SAAA,cACE;AAAA,EAAA;AAAA,EAEF,IAAI,sBAAqD;AACvD,WAAO;AAAA,EACT;AAAA,EAEA,MAAgB,aACd,QACA,aACkB;AAClB,YAAQ,IAAI,0DAA0D,OAAO,QAAQ,EAAE;AAGvF,QAAI;AACJ,QAAI;AACF,cAAQ,IAAI,0BAA0B,OAAO,QAAQ,EAAE;AACvD,cAAQ,IAAI,sCAAsC,QAAQ,IAAA,CAAK,EAAE;AAEjE,YAAM,QAAQ,MAAM,GAAG,KAAK,OAAO,QAAQ;AAC3C,UAAI,CAAC,MAAM,UAAU;AACnB,cAAM,IAAI,MAAM,SAAS,OAAO,QAAQ,iBAAiB;AAAA,MAC3D;AAEA,cAAQ,IAAI,sBAAsB,MAAM,IAAI,QAAQ;AAEpD,UAAI,MAAM,SAAS,GAAG;AACpB,cAAM,IAAI;AAAA,UACR,SAAS,OAAO,QAAQ;AAAA,QAAA;AAAA,MAE5B;AAEA,UAAI,MAAM,OAAO,IAAI;AACnB,cAAM,IAAI;AAAA,UACR,SAAS,OAAO,QAAQ,mBAAmB,MAAM,IAAI;AAAA,QAAA;AAAA,MAEzD;AAEA,UAAI,MAAM,OAAO,MAAM,OAAO,MAAM;AAClC,gBAAQ,IAAI,yDAAyD,MAAM,QAAQ,OAAO,OAAO,QAAQ,CAAC,CAAC,MAAM;AAAA,MACnH;AAEA,WAAK,QAAQ,KAAK,yBAAyB;AAC3C,oBAAc,MAAM,GAAG,SAAS,OAAO,QAAQ;AAC/C,WAAK,QAAQ,KAAK,QAAQ,YAAY,MAAM,kBAAkB;AAE9D,UAAI,CAAC,eAAe,YAAY,WAAW,GAAG;AAC5C,cAAM,IAAI;AAAA,UACR,SAAS,OAAO,QAAQ;AAAA,QAAA;AAAA,MAE5B;AAEA,UAAI,YAAY,SAAS,IAAI;AAC3B,cAAM,IAAI;AAAA,UACR,SAAS,OAAO,QAAQ,2BAA2B,YAAY,MAAM;AAAA,QAAA;AAAA,MAEzE;AAEA,YAAMA,YAAW,KAAK,SAAS,OAAO,QAAQ;AAC9C,YAAMC,YAAW,KAAK,YAAYD,SAAQ;AAC1C,UAAIC,UAAS,WAAW,OAAO,KAAKA,cAAa,oBAAoB;AACnE,cAAM,cAAc,YAAY,SAAS,QAAQ,GAAG,KAAK,IAAI,YAAY,QAAQ,GAAI,CAAC;AACtF,YAAI,YAAY,KAAA,MAAW,IAAI;AAC7B,gBAAM,IAAI;AAAA,YACR,SAAS,OAAO,QAAQ;AAAA,UAAA;AAAA,QAE5B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,YAAI,MAAM,QAAQ,SAAS,QAAQ,GAAG;AACpC,gBAAM,IAAI,MAAM,oBAAoB,OAAO,QAAQ,GAAG;AAAA,QACxD;AACA,cAAM;AAAA,MACR;AACA,YAAM,IAAI,MAAM,wBAAwB,KAAK,EAAE;AAAA,IACjD;AAEA,UAAM,aAAa,YAAY,SAAS,QAAQ;AAChD,SAAK,QAAQ,KAAK,wBAAwB,WAAW,MAAM,aAAa;AAExE,UAAM,WAAW,KAAK,SAAS,OAAO,QAAQ;AAC9C,UAAM,WAAW,KAAK,YAAY,QAAQ;AAC1C,SAAK,QAAQ,KAAK,SAAS,QAAQ,gBAAgB,QAAQ,EAAE;AAE7D,UAAM,UAA8B;AAAA,MAClC,MAAM,OAAO;AAAA,MACb,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO;AAAA,MACb,WAAW,OAAO;AAAA,MAClB,qBAAqB,OAAO,uBAAuB;AAAA,MACnD,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,QAAQ,OAAO;AAAA,MACf,SAAS,KAAK,iBAAiB,WAAW,EAAE,OAAO,QAChD,WACA,SAAS,SAAS,IACjB,YACA;AAAA,IAAA;AAGN,QAAI;AACF,UAAI;AAEJ,UAAI,OAAO,WAAW;AACpB,cAAM,iBAAiB,IAAI,QAAQ,CAAC,GAAG,WAAW;AAChD;AAAA,YACE,MAAM,OAAO,IAAI,MAAM,+BAA+B,OAAO,SAAS,IAAI,CAAC;AAAA,YAC3E,OAAO;AAAA,UAAA;AAAA,QAEX,CAAC;AAED,iBAAS,MAAM,QAAQ,KAAK;AAAA,UAC1B,KAAK,iBAAiB;AAAA,YACpB;AAAA,cACE,MAAM;AAAA,cACN,QAAQ,OAAO,KAAK,YAAY,QAAQ;AAAA,cACxC;AAAA,cACA;AAAA,YAAA;AAAA,YAEF;AAAA,UAAA;AAAA,UAEF;AAAA,QAAA,CACD;AAAA,MACH,OAAO;AACL,iBAAS,MAAM,KAAK,iBAAiB;AAAA,UACnC;AAAA,YACE,MAAM;AAAA,YACN,QAAQ,OAAO,KAAK,YAAY,QAAQ;AAAA,YACxC;AAAA,YACA;AAAA,UAAA;AAAA,UAEF;AAAA,QAAA;AAAA,MAEJ;AAEA,UAAI,OAAO,WAAW;AACpB,cAAM,UAAU,OAAO,aAAa,YAAY,OAAO,OAAO;AAC9D,cAAM,UAAU,QAAQ,WAAW;AACnC,cAAM,SAAS,UACX,8CAA8C,OAAO,YAAY,OAAO,KACxE;AACJ,eAAO;AAAA;AAAA,kBACL,OAAO,OAAO,aAChB;AAAA,YAAe,WAAW,KAAK,GAC7B,SAAS;AAAA,oBAAuB,MAAM,KAAK,EAC7C;AAAA;AAAA;AAAA,MACF,OAAO;AACL,eAAO;AAAA;AAAA,kBAAgF,OAAO,OAAO,aAAa;AAAA;AAAA;AAAA,MACpH;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,YAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;AAAA,IACvD;AAAA,EACF;AAAA,EAEQ,YAAY,UAA0B;AAC5C,UAAM,MAAM,KAAK,QAAQ,QAAQ,EAAE,YAAA;AACnC,UAAM,YAAoC;AAAA,MACxC,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,QAAQ;AAAA,IAAA;AAEV,WAAO,UAAU,GAAG,KAAK;AAAA,EAC3B;AACF;"}
|
|
1
|
+
{"version":3,"file":"standards-agent-kit.es35.js","sources":["../../src/tools/inscriber/InscribeFromFileTool.ts"],"sourcesContent":["import { z } from 'zod';\nimport { BaseInscriberQueryTool } from './base-inscriber-tools';\nimport { InscriptionOptions } from '@hashgraphonline/standards-sdk';\nimport { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\n\n/**\n * Schema for inscribing from file\n */\nconst inscribeFromFileSchema = z.object({\n filePath: z\n .string()\n .min(1, 'File path cannot be empty')\n .describe(\n 'The file path of the content to inscribe. Must point to a valid, non-empty file.'\n ),\n mode: z\n .enum(['file', 'hashinal'])\n .optional()\n .describe('Inscription mode: file or hashinal NFT'),\n metadata: z\n .record(z.unknown())\n .optional()\n .describe('Metadata to attach to the inscription'),\n tags: z\n .array(z.string())\n .optional()\n .describe('Tags to categorize the inscription'),\n chunkSize: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Chunk size for large files'),\n waitForConfirmation: z\n .boolean()\n .optional()\n .describe('Whether to wait for inscription confirmation'),\n timeoutMs: z\n .number()\n .int()\n .positive()\n .optional()\n .describe('Timeout in milliseconds for inscription (default: no timeout)'),\n apiKey: z.string().optional().describe('API key for inscription service'),\n quoteOnly: z\n .boolean()\n .optional()\n .default(false)\n .describe(\n 'If true, returns a cost quote instead of executing the inscription'\n ),\n});\n\n/**\n * Tool for inscribing content from file\n */\nexport class InscribeFromFileTool extends BaseInscriberQueryTool<\n typeof inscribeFromFileSchema\n> {\n name = 'inscribeFromFile';\n description =\n '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.';\n\n get specificInputSchema(): typeof inscribeFromFileSchema {\n return inscribeFromFileSchema;\n }\n\n protected async executeQuery(\n params: z.infer<typeof inscribeFromFileSchema>,\n _runManager?: CallbackManagerForToolRun\n ): Promise<unknown> {\n console.log(\n `[DEBUG] InscribeFromFileTool.executeQuery called with: ${params.filePath}`\n );\n\n let fileContent: Buffer;\n try {\n console.log(`[DEBUG] Checking file: ${params.filePath}`);\n console.log(`[DEBUG] Current working directory: ${process.cwd()}`);\n\n const stats = await fs.stat(params.filePath);\n if (!stats.isFile()) {\n throw new Error(`Path \"${params.filePath}\" is not a file`);\n }\n\n console.log(`[DEBUG] File size: ${stats.size} bytes`);\n\n if (stats.size === 0) {\n throw new Error(\n `File \"${params.filePath}\" is empty (0 bytes). Cannot inscribe empty files.`\n );\n }\n\n if (stats.size < 10) {\n throw new Error(\n `File \"${params.filePath}\" is too small (${stats.size} bytes). Files must contain at least 10 bytes of meaningful content.`\n );\n }\n\n if (stats.size > 100 * 1024 * 1024) {\n console.log(\n `[InscribeFromFileTool] WARNING: Large file detected (${(\n stats.size /\n (1024 * 1024)\n ).toFixed(2)} MB)`\n );\n }\n\n this.logger?.info('Reading file content...');\n fileContent = await fs.readFile(params.filePath);\n this.logger?.info(`Read ${fileContent.length} bytes from file`);\n\n if (!fileContent || fileContent.length === 0) {\n throw new Error(\n `File \"${params.filePath}\" has no content after reading. Cannot inscribe empty files.`\n );\n }\n\n if (fileContent.length < 10) {\n throw new Error(\n `File \"${params.filePath}\" content is too small (${fileContent.length} bytes). Files must contain at least 10 bytes of meaningful content.`\n );\n }\n\n const fileName = path.basename(params.filePath);\n const mimeType = this.getMimeType(fileName);\n if (mimeType.startsWith('text/') || mimeType === 'application/json') {\n const textContent = fileContent.toString(\n 'utf8',\n 0,\n Math.min(fileContent.length, 1000)\n );\n if (textContent.trim() === '') {\n throw new Error(\n `File \"${params.filePath}\" contains only whitespace or empty content. Cannot inscribe meaningless data.`\n );\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n throw new Error(`File not found: \"${params.filePath}\"`);\n }\n throw error;\n }\n throw new Error(`Failed to read file: ${error}`);\n }\n\n const base64Data = fileContent.toString('base64');\n this.logger?.info(`Converted to base64: ${base64Data.length} characters`);\n\n const fileName = path.basename(params.filePath);\n const mimeType = this.getMimeType(fileName);\n this.logger?.info(`File: ${fileName}, MIME type: ${mimeType}`);\n\n const options: InscriptionOptions = {\n mode: params.mode,\n metadata: params.metadata,\n tags: params.tags,\n chunkSize: params.chunkSize,\n waitForConfirmation: params.quoteOnly\n ? false\n : params.waitForConfirmation ?? true,\n waitMaxAttempts: 10,\n waitIntervalMs: 3000,\n apiKey: params.apiKey,\n network: this.inscriberBuilder['hederaKit'].client.network\n .toString()\n .includes('mainnet')\n ? 'mainnet'\n : 'testnet',\n quoteOnly: params.quoteOnly,\n };\n\n if (params.quoteOnly) {\n try {\n const quote = await this.generateInscriptionQuote(\n {\n type: 'buffer',\n buffer: Buffer.from(base64Data, 'base64'),\n fileName,\n mimeType,\n },\n options\n );\n\n return {\n success: true,\n quote: {\n totalCostHbar: quote.totalCostHbar,\n validUntil: quote.validUntil,\n breakdown: quote.breakdown,\n },\n contentInfo: {\n fileName,\n mimeType,\n sizeBytes: fileContent.length,\n filePath: params.filePath,\n },\n message: `Quote generated for file: ${fileName} (${(\n fileContent.length / 1024\n ).toFixed(2)} KB)\\nTotal cost: ${\n quote.totalCostHbar\n } HBAR`,\n };\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : 'Failed to generate inscription quote';\n throw new Error(`Quote generation failed: ${errorMessage}`);\n }\n }\n\n try {\n let result: unknown;\n\n if (params.timeoutMs) {\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(\n () =>\n reject(\n new Error(`Inscription timed out after ${params.timeoutMs}ms`)\n ),\n params.timeoutMs\n );\n });\n\n result = await Promise.race([\n this.inscriberBuilder.inscribe(\n {\n type: 'buffer',\n buffer: Buffer.from(base64Data, 'base64'),\n fileName,\n mimeType,\n },\n options\n ),\n timeoutPromise,\n ]);\n } else {\n result = await this.inscriberBuilder.inscribe(\n {\n type: 'buffer',\n buffer: Buffer.from(base64Data, 'base64'),\n fileName,\n mimeType,\n },\n options\n );\n }\n\n const inscriptionResult = result as any;\n if (inscriptionResult.confirmed && !inscriptionResult.quote) {\n const topicId =\n inscriptionResult.inscription?.topic_id ||\n inscriptionResult.result.topicId;\n const network = options.network || 'testnet';\n const cdnUrl = topicId\n ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}`\n : null;\n return `Successfully inscribed and confirmed content on the Hedera network!\\n\\nTransaction ID: ${\n inscriptionResult.result.transactionId\n }\\nTopic ID: ${topicId || 'N/A'}${\n cdnUrl ? `\\nView inscription: ${cdnUrl}` : ''\n }\\n\\nThe inscription is now available.`;\n } else if (!inscriptionResult.quote && !inscriptionResult.confirmed) {\n return `Successfully submitted inscription to the Hedera network!\\n\\nTransaction ID: ${inscriptionResult.result.transactionId}\\n\\nThe inscription is processing and will be confirmed shortly.`;\n } else {\n return 'Inscription operation completed.';\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Failed to inscribe from file';\n throw new Error(`Inscription failed: ${errorMessage}`);\n }\n }\n\n private getMimeType(fileName: string): string {\n const ext = path.extname(fileName).toLowerCase();\n const mimeTypes: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.gif': 'image/gif',\n '.webp': 'image/webp',\n '.svg': 'image/svg+xml',\n '.pdf': 'application/pdf',\n '.json': 'application/json',\n '.txt': 'text/plain',\n '.html': 'text/html',\n '.css': 'text/css',\n '.js': 'application/javascript',\n '.ts': 'application/typescript',\n '.mp4': 'video/mp4',\n '.mp3': 'audio/mpeg',\n '.wav': 'audio/wav',\n '.zip': 'application/zip',\n };\n return mimeTypes[ext] || 'application/octet-stream';\n }\n}\n"],"names":["fileName","mimeType"],"mappings":"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"}
|