@hashgraphonline/standards-agent-kit 0.2.119 → 0.2.121
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/config/ContentReferenceConfig.d.ts +8 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/standards-agent-kit.cjs +1 -1
- package/dist/cjs/standards-agent-kit.cjs.map +1 -1
- package/dist/cjs/tools/hcs6/CreateDynamicRegistryTool.d.ts +7 -7
- package/dist/cjs/tools/hcs6/QueryDynamicRegistryTool.d.ts +3 -3
- package/dist/cjs/tools/hcs6/RegisterDynamicHashinalTool.d.ts +7 -7
- package/dist/cjs/tools/hcs6/UpdateDynamicHashinalTool.d.ts +7 -7
- package/dist/cjs/tools/inscriber/InscribeFromBufferTool.d.ts +7 -6
- package/dist/cjs/tools/inscriber/base-inscriber-tools.d.ts +11 -0
- package/dist/cjs/tools/inscriber/inscriber-tool-params.d.ts +3 -0
- package/dist/cjs/types/content-resolver.d.ts +6 -0
- package/dist/es/config/ContentReferenceConfig.d.ts +8 -0
- package/dist/es/index.d.ts +2 -0
- package/dist/es/standards-agent-kit.es.js +42 -40
- package/dist/es/standards-agent-kit.es.js.map +1 -1
- package/dist/es/standards-agent-kit.es10.js +72 -23
- package/dist/es/standards-agent-kit.es10.js.map +1 -1
- package/dist/es/standards-agent-kit.es11.js +27 -27
- package/dist/es/standards-agent-kit.es11.js.map +1 -1
- package/dist/es/standards-agent-kit.es12.js +20 -30
- package/dist/es/standards-agent-kit.es12.js.map +1 -1
- package/dist/es/standards-agent-kit.es13.js +27 -37
- package/dist/es/standards-agent-kit.es13.js.map +1 -1
- package/dist/es/standards-agent-kit.es14.js +48 -60
- package/dist/es/standards-agent-kit.es14.js.map +1 -1
- package/dist/es/standards-agent-kit.es15.js +60 -23
- package/dist/es/standards-agent-kit.es15.js.map +1 -1
- package/dist/es/standards-agent-kit.es16.js +23 -23
- package/dist/es/standards-agent-kit.es16.js.map +1 -1
- package/dist/es/standards-agent-kit.es17.js +23 -33
- package/dist/es/standards-agent-kit.es17.js.map +1 -1
- package/dist/es/standards-agent-kit.es18.js +34 -10
- package/dist/es/standards-agent-kit.es18.js.map +1 -1
- package/dist/es/standards-agent-kit.es19.js +16 -155
- package/dist/es/standards-agent-kit.es19.js.map +1 -1
- package/dist/es/standards-agent-kit.es2.js +21 -1567
- package/dist/es/standards-agent-kit.es2.js.map +1 -1
- package/dist/es/standards-agent-kit.es20.js +150 -22
- package/dist/es/standards-agent-kit.es20.js.map +1 -1
- package/dist/es/standards-agent-kit.es21.js +25 -39
- package/dist/es/standards-agent-kit.es21.js.map +1 -1
- package/dist/es/standards-agent-kit.es22.js +26 -28
- package/dist/es/standards-agent-kit.es22.js.map +1 -1
- package/dist/es/standards-agent-kit.es23.js +16 -19
- package/dist/es/standards-agent-kit.es23.js.map +1 -1
- package/dist/es/standards-agent-kit.es24.js +21 -15
- package/dist/es/standards-agent-kit.es24.js.map +1 -1
- package/dist/es/standards-agent-kit.es25.js +18 -21
- package/dist/es/standards-agent-kit.es25.js.map +1 -1
- package/dist/es/standards-agent-kit.es26.js +35 -32
- package/dist/es/standards-agent-kit.es26.js.map +1 -1
- package/dist/es/standards-agent-kit.es27.js +38 -25
- package/dist/es/standards-agent-kit.es27.js.map +1 -1
- package/dist/es/standards-agent-kit.es28.js +24 -37
- package/dist/es/standards-agent-kit.es28.js.map +1 -1
- package/dist/es/standards-agent-kit.es29.js +26 -43
- package/dist/es/standards-agent-kit.es29.js.map +1 -1
- package/dist/es/standards-agent-kit.es3.js +1532 -55
- package/dist/es/standards-agent-kit.es3.js.map +1 -1
- package/dist/es/standards-agent-kit.es30.js +35 -40
- package/dist/es/standards-agent-kit.es30.js.map +1 -1
- package/dist/es/standards-agent-kit.es31.js +35 -39
- package/dist/es/standards-agent-kit.es31.js.map +1 -1
- package/dist/es/standards-agent-kit.es32.js +39 -23
- package/dist/es/standards-agent-kit.es32.js.map +1 -1
- package/dist/es/standards-agent-kit.es33.js +39 -168
- package/dist/es/standards-agent-kit.es33.js.map +1 -1
- package/dist/es/standards-agent-kit.es34.js +93 -96
- package/dist/es/standards-agent-kit.es34.js.map +1 -1
- package/dist/es/standards-agent-kit.es35.js +99 -64
- package/dist/es/standards-agent-kit.es35.js.map +1 -1
- package/dist/es/standards-agent-kit.es36.js +166 -64
- package/dist/es/standards-agent-kit.es36.js.map +1 -1
- package/dist/es/standards-agent-kit.es37.js +87 -30
- package/dist/es/standards-agent-kit.es37.js.map +1 -1
- package/dist/es/standards-agent-kit.es38.js +38 -242
- package/dist/es/standards-agent-kit.es38.js.map +1 -1
- package/dist/es/standards-agent-kit.es39.js +246 -138
- package/dist/es/standards-agent-kit.es39.js.map +1 -1
- package/dist/es/standards-agent-kit.es4.js +39 -33
- package/dist/es/standards-agent-kit.es4.js.map +1 -1
- package/dist/es/standards-agent-kit.es40.js +134 -26
- package/dist/es/standards-agent-kit.es40.js.map +1 -1
- package/dist/es/standards-agent-kit.es41.js +27 -22
- package/dist/es/standards-agent-kit.es41.js.map +1 -1
- package/dist/es/standards-agent-kit.es42.js +24 -3
- package/dist/es/standards-agent-kit.es42.js.map +1 -1
- package/dist/es/standards-agent-kit.es43.js +7 -0
- package/dist/es/standards-agent-kit.es43.js.map +1 -0
- package/dist/es/standards-agent-kit.es5.js +65 -32
- package/dist/es/standards-agent-kit.es5.js.map +1 -1
- package/dist/es/standards-agent-kit.es6.js +35 -311
- package/dist/es/standards-agent-kit.es6.js.map +1 -1
- package/dist/es/standards-agent-kit.es7.js +320 -20
- package/dist/es/standards-agent-kit.es7.js.map +1 -1
- package/dist/es/standards-agent-kit.es8.js +19 -227
- package/dist/es/standards-agent-kit.es8.js.map +1 -1
- package/dist/es/standards-agent-kit.es9.js +216 -65
- package/dist/es/standards-agent-kit.es9.js.map +1 -1
- package/dist/es/tools/hcs6/CreateDynamicRegistryTool.d.ts +7 -7
- package/dist/es/tools/hcs6/QueryDynamicRegistryTool.d.ts +3 -3
- package/dist/es/tools/hcs6/RegisterDynamicHashinalTool.d.ts +7 -7
- package/dist/es/tools/hcs6/UpdateDynamicHashinalTool.d.ts +7 -7
- package/dist/es/tools/inscriber/InscribeFromBufferTool.d.ts +7 -6
- package/dist/es/tools/inscriber/base-inscriber-tools.d.ts +11 -0
- package/dist/es/tools/inscriber/inscriber-tool-params.d.ts +3 -0
- package/dist/es/types/content-resolver.d.ts +6 -0
- package/dist/umd/config/ContentReferenceConfig.d.ts +8 -0
- package/dist/umd/index.d.ts +2 -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/hcs6/CreateDynamicRegistryTool.d.ts +7 -7
- package/dist/umd/tools/hcs6/QueryDynamicRegistryTool.d.ts +3 -3
- package/dist/umd/tools/hcs6/RegisterDynamicHashinalTool.d.ts +7 -7
- package/dist/umd/tools/hcs6/UpdateDynamicHashinalTool.d.ts +7 -7
- package/dist/umd/tools/inscriber/InscribeFromBufferTool.d.ts +7 -6
- package/dist/umd/tools/inscriber/base-inscriber-tools.d.ts +11 -0
- package/dist/umd/tools/inscriber/inscriber-tool-params.d.ts +3 -0
- package/dist/umd/types/content-resolver.d.ts +6 -0
- package/package.json +2 -2
- package/src/builders/hcs10/hcs10-builder.ts +2 -3
- package/src/config/ContentReferenceConfig.ts +30 -0
- package/src/index.ts +3 -1
- package/src/tools/hcs6/CreateDynamicRegistryTool.ts +22 -33
- package/src/tools/hcs6/QueryDynamicRegistryTool.ts +23 -33
- package/src/tools/hcs6/RegisterDynamicHashinalTool.ts +25 -35
- package/src/tools/hcs6/UpdateDynamicHashinalTool.ts +25 -36
- package/src/tools/inscriber/InscribeFromBufferTool.ts +239 -120
- package/src/tools/inscriber/base-inscriber-tools.ts +19 -0
- package/src/tools/inscriber/inscriber-tool-params.ts +3 -0
- package/src/types/content-resolver.ts +11 -0
|
@@ -1,175 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
chunkSize: z.number().int().positive().optional().describe("Chunk size for large files"),
|
|
9
|
-
waitForConfirmation: z.boolean().optional().describe("Whether to wait for inscription confirmation"),
|
|
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")
|
|
12
|
-
});
|
|
13
|
-
class InscribeFromUrlTool extends BaseInscriberQueryTool {
|
|
14
|
-
constructor() {
|
|
15
|
-
super(...arguments);
|
|
16
|
-
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.';
|
|
1
|
+
import { BaseHederaTransactionTool, BaseHederaQueryTool } from "hedera-agent-kit";
|
|
2
|
+
class BaseInscriberTransactionTool extends BaseHederaTransactionTool {
|
|
3
|
+
constructor(params) {
|
|
4
|
+
super(params);
|
|
5
|
+
this.namespace = "inscriber";
|
|
6
|
+
this.inscriberBuilder = params.inscriberBuilder;
|
|
7
|
+
this.contentResolver = params.contentResolver || null;
|
|
18
8
|
}
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Override to return the InscriberBuilder
|
|
11
|
+
*/
|
|
12
|
+
getServiceBuilder() {
|
|
13
|
+
return this.inscriberBuilder;
|
|
21
14
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
method: "HEAD",
|
|
48
|
-
signal: controller.signal
|
|
49
|
-
});
|
|
50
|
-
clearTimeout(timeoutId);
|
|
51
|
-
if (!headResponse.ok) {
|
|
52
|
-
throw new Error(`URL returned error status ${headResponse.status}: ${headResponse.statusText}. Cannot inscribe content from inaccessible URLs.`);
|
|
53
|
-
}
|
|
54
|
-
const contentType = headResponse.headers.get("content-type") || "";
|
|
55
|
-
const contentLength = headResponse.headers.get("content-length");
|
|
56
|
-
const webPageContentTypes = [
|
|
57
|
-
"text/html",
|
|
58
|
-
"application/xhtml+xml",
|
|
59
|
-
"text/xml"
|
|
60
|
-
];
|
|
61
|
-
if (webPageContentTypes.some((type) => contentType.toLowerCase().includes(type))) {
|
|
62
|
-
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.`);
|
|
63
|
-
}
|
|
64
|
-
if (contentLength && parseInt(contentLength) === 0) {
|
|
65
|
-
throw new Error("URL returns empty content (Content-Length: 0). Cannot inscribe empty content.");
|
|
66
|
-
}
|
|
67
|
-
if (contentLength && parseInt(contentLength) < 10) {
|
|
68
|
-
throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);
|
|
69
|
-
}
|
|
70
|
-
if (!contentType || contentType === "application/octet-stream") {
|
|
71
|
-
console.log(`[InscribeFromUrlTool] Content-Type unclear, fetching first 1KB to verify...`);
|
|
72
|
-
const getController = new AbortController();
|
|
73
|
-
const getTimeoutId = setTimeout(() => getController.abort(), 5e3);
|
|
74
|
-
try {
|
|
75
|
-
const getResponse = await fetch(params.url, {
|
|
76
|
-
signal: getController.signal,
|
|
77
|
-
headers: {
|
|
78
|
-
"Range": "bytes=0-1023"
|
|
79
|
-
// Get first 1KB
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
clearTimeout(getTimeoutId);
|
|
83
|
-
if (getResponse.ok || getResponse.status === 206) {
|
|
84
|
-
const buffer = await getResponse.arrayBuffer();
|
|
85
|
-
const bytes = new Uint8Array(buffer);
|
|
86
|
-
const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes.slice(0, 512));
|
|
87
|
-
if (text.toLowerCase().includes("<!doctype html") || text.toLowerCase().includes("<html") || text.match(/<meta\s+[^>]*>/i) || text.match(/<title>/i)) {
|
|
88
|
-
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.`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} catch (getError) {
|
|
92
|
-
clearTimeout(getTimeoutId);
|
|
93
|
-
if (getError instanceof Error && getError.message.includes("HTML content")) {
|
|
94
|
-
throw getError;
|
|
95
|
-
}
|
|
96
|
-
console.log(`[InscribeFromUrlTool] Could not perform partial GET validation: ${getError instanceof Error ? getError.message : "Unknown error"}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
console.log(`[InscribeFromUrlTool] URL validation passed. Content-Type: ${contentType}, Content-Length: ${contentLength || "unknown"}`);
|
|
100
|
-
} catch (fetchError) {
|
|
101
|
-
clearTimeout(timeoutId);
|
|
102
|
-
throw fetchError;
|
|
103
|
-
}
|
|
104
|
-
} catch (error) {
|
|
105
|
-
if (error instanceof Error) {
|
|
106
|
-
if (error.name === "AbortError") {
|
|
107
|
-
console.log(`[InscribeFromUrlTool] Warning: URL validation timed out after 10 seconds. Proceeding with inscription attempt.`);
|
|
108
|
-
} else if (error.message.includes("URL returned error") || error.message.includes("empty content") || error.message.includes("too small") || error.message.includes("HTML")) {
|
|
109
|
-
throw error;
|
|
110
|
-
} else {
|
|
111
|
-
console.log(`[InscribeFromUrlTool] Warning: Could not validate URL with HEAD request: ${error.message}. Proceeding with inscription attempt.`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const options = {
|
|
116
|
-
mode: params.mode,
|
|
117
|
-
metadata: params.metadata,
|
|
118
|
-
tags: params.tags,
|
|
119
|
-
chunkSize: params.chunkSize,
|
|
120
|
-
waitForConfirmation: params.waitForConfirmation ?? true,
|
|
121
|
-
waitMaxAttempts: 10,
|
|
122
|
-
waitIntervalMs: 3e3,
|
|
123
|
-
apiKey: params.apiKey,
|
|
124
|
-
network: this.inscriberBuilder["hederaKit"].client.network.toString().includes("mainnet") ? "mainnet" : "testnet"
|
|
125
|
-
};
|
|
126
|
-
try {
|
|
127
|
-
let result;
|
|
128
|
-
if (params.timeoutMs) {
|
|
129
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
130
|
-
setTimeout(
|
|
131
|
-
() => reject(new Error(`Inscription timed out after ${params.timeoutMs}ms`)),
|
|
132
|
-
params.timeoutMs
|
|
133
|
-
);
|
|
134
|
-
});
|
|
135
|
-
result = await Promise.race([
|
|
136
|
-
this.inscriberBuilder.inscribe(
|
|
137
|
-
{ type: "url", url: params.url },
|
|
138
|
-
options
|
|
139
|
-
),
|
|
140
|
-
timeoutPromise
|
|
141
|
-
]);
|
|
142
|
-
} else {
|
|
143
|
-
result = await this.inscriberBuilder.inscribe(
|
|
144
|
-
{ type: "url", url: params.url },
|
|
145
|
-
options
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
if (result.confirmed) {
|
|
149
|
-
const topicId = result.inscription?.topic_id || result.result.topicId;
|
|
150
|
-
const network = options.network || "testnet";
|
|
151
|
-
const cdnUrl = topicId ? `https://kiloscribe.com/api/inscription-cdn/${topicId}?network=${network}` : null;
|
|
152
|
-
return `Successfully inscribed and confirmed content on the Hedera network!
|
|
153
|
-
|
|
154
|
-
Transaction ID: ${result.result.transactionId}
|
|
155
|
-
Topic ID: ${topicId || "N/A"}${cdnUrl ? `
|
|
156
|
-
View inscription: ${cdnUrl}` : ""}
|
|
157
|
-
|
|
158
|
-
The inscription is now available.`;
|
|
159
|
-
} else {
|
|
160
|
-
return `Successfully submitted inscription to the Hedera network!
|
|
161
|
-
|
|
162
|
-
Transaction ID: ${result.result.transactionId}
|
|
163
|
-
|
|
164
|
-
The inscription is processing and will be confirmed shortly.`;
|
|
165
|
-
}
|
|
166
|
-
} catch (error) {
|
|
167
|
-
const errorMessage = error instanceof Error ? error.message : "Failed to inscribe from URL";
|
|
168
|
-
throw new Error(`Inscription failed: ${errorMessage}`);
|
|
169
|
-
}
|
|
15
|
+
/**
|
|
16
|
+
* Get content resolver with fallback to registry
|
|
17
|
+
*/
|
|
18
|
+
getContentResolver() {
|
|
19
|
+
return this.contentResolver;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
class BaseInscriberQueryTool extends BaseHederaQueryTool {
|
|
23
|
+
constructor(params) {
|
|
24
|
+
super(params);
|
|
25
|
+
this.namespace = "inscriber";
|
|
26
|
+
this.inscriberBuilder = params.inscriberBuilder;
|
|
27
|
+
this.contentResolver = params.contentResolver || null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Override to return the InscriberBuilder
|
|
31
|
+
*/
|
|
32
|
+
getServiceBuilder() {
|
|
33
|
+
return this.inscriberBuilder;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get content resolver with fallback to registry
|
|
37
|
+
*/
|
|
38
|
+
getContentResolver() {
|
|
39
|
+
return this.contentResolver;
|
|
170
40
|
}
|
|
171
41
|
}
|
|
172
42
|
export {
|
|
173
|
-
|
|
43
|
+
BaseInscriberQueryTool,
|
|
44
|
+
BaseInscriberTransactionTool
|
|
174
45
|
};
|
|
175
46
|
//# sourceMappingURL=standards-agent-kit.es33.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-agent-kit.es33.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":"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"}
|
|
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":";AAgBO,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;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;AACF;"}
|
|
@@ -1,87 +1,117 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { BaseInscriberQueryTool } from "./standards-agent-kit.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const inscribeFromFileSchema = z.object({
|
|
6
|
-
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."),
|
|
2
|
+
import { BaseInscriberQueryTool } from "./standards-agent-kit.es33.js";
|
|
3
|
+
const inscribeFromUrlSchema = z.object({
|
|
4
|
+
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."),
|
|
7
5
|
mode: z.enum(["file", "hashinal"]).optional().describe("Inscription mode: file or hashinal NFT"),
|
|
8
6
|
metadata: z.record(z.unknown()).optional().describe("Metadata to attach to the inscription"),
|
|
9
7
|
tags: z.array(z.string()).optional().describe("Tags to categorize the inscription"),
|
|
10
8
|
chunkSize: z.number().int().positive().optional().describe("Chunk size for large files"),
|
|
11
9
|
waitForConfirmation: z.boolean().optional().describe("Whether to wait for inscription confirmation"),
|
|
12
|
-
timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds for inscription (default: no timeout)"),
|
|
10
|
+
timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds for inscription (default: no timeout - waits until completion)"),
|
|
13
11
|
apiKey: z.string().optional().describe("API key for inscription service")
|
|
14
12
|
});
|
|
15
|
-
class
|
|
13
|
+
class InscribeFromUrlTool extends BaseInscriberQueryTool {
|
|
16
14
|
constructor() {
|
|
17
15
|
super(...arguments);
|
|
18
|
-
this.name = "
|
|
19
|
-
this.description =
|
|
16
|
+
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.';
|
|
20
18
|
}
|
|
21
19
|
get specificInputSchema() {
|
|
22
|
-
return
|
|
20
|
+
return inscribeFromUrlSchema;
|
|
23
21
|
}
|
|
24
22
|
async executeQuery(params, _runManager) {
|
|
25
|
-
console.log(`[DEBUG]
|
|
26
|
-
|
|
23
|
+
console.log(`[DEBUG] InscribeFromUrlTool.executeQuery called with URL: ${params.url}`);
|
|
24
|
+
if (!params.url || params.url.trim() === "") {
|
|
25
|
+
throw new Error("URL cannot be empty. Please provide a valid URL.");
|
|
26
|
+
}
|
|
27
27
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!stats.isFile()) {
|
|
32
|
-
throw new Error(`Path "${params.filePath}" is not a file`);
|
|
33
|
-
}
|
|
34
|
-
console.log(`[DEBUG] File size: ${stats.size} bytes`);
|
|
35
|
-
if (stats.size === 0) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`File "${params.filePath}" is empty (0 bytes). Cannot inscribe empty files.`
|
|
38
|
-
);
|
|
28
|
+
const urlObj = new URL(params.url);
|
|
29
|
+
if (!urlObj.protocol || !urlObj.host) {
|
|
30
|
+
throw new Error("Invalid URL format. Please provide a complete URL with protocol (http/https).");
|
|
39
31
|
}
|
|
40
|
-
if (
|
|
41
|
-
throw new Error(
|
|
42
|
-
`File "${params.filePath}" is too small (${stats.size} bytes). Files must contain at least 10 bytes of meaningful content.`
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
if (stats.size > 100 * 1024 * 1024) {
|
|
46
|
-
console.log(`[InscribeFromFileTool] WARNING: Large file detected (${(stats.size / (1024 * 1024)).toFixed(2)} MB)`);
|
|
32
|
+
if (!["http:", "https:"].includes(urlObj.protocol)) {
|
|
33
|
+
throw new Error("Only HTTP and HTTPS URLs are supported for inscription.");
|
|
47
34
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (!fileContent || fileContent.length === 0) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
`File "${params.filePath}" has no content after reading. Cannot inscribe empty files.`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
if (fileContent.length < 10) {
|
|
57
|
-
throw new Error(
|
|
58
|
-
`File "${params.filePath}" content is too small (${fileContent.length} bytes). Files must contain at least 10 bytes of meaningful content.`
|
|
59
|
-
);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof Error && error.message.includes("Cannot inscribe content from")) {
|
|
37
|
+
throw error;
|
|
60
38
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
39
|
+
throw new Error(`Invalid URL: ${params.url}. Please provide a valid URL.`);
|
|
40
|
+
}
|
|
41
|
+
console.log(`[InscribeFromUrlTool] Validating URL content before inscription...`);
|
|
42
|
+
try {
|
|
43
|
+
const controller = new AbortController();
|
|
44
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
45
|
+
try {
|
|
46
|
+
const headResponse = await fetch(params.url, {
|
|
47
|
+
method: "HEAD",
|
|
48
|
+
signal: controller.signal
|
|
49
|
+
});
|
|
50
|
+
clearTimeout(timeoutId);
|
|
51
|
+
if (!headResponse.ok) {
|
|
52
|
+
throw new Error(`URL returned error status ${headResponse.status}: ${headResponse.statusText}. Cannot inscribe content from inaccessible URLs.`);
|
|
53
|
+
}
|
|
54
|
+
const contentType = headResponse.headers.get("content-type") || "";
|
|
55
|
+
const contentLength = headResponse.headers.get("content-length");
|
|
56
|
+
const webPageContentTypes = [
|
|
57
|
+
"text/html",
|
|
58
|
+
"application/xhtml+xml",
|
|
59
|
+
"text/xml"
|
|
60
|
+
];
|
|
61
|
+
if (webPageContentTypes.some((type) => contentType.toLowerCase().includes(type))) {
|
|
62
|
+
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.`);
|
|
63
|
+
}
|
|
64
|
+
if (contentLength && parseInt(contentLength) === 0) {
|
|
65
|
+
throw new Error("URL returns empty content (Content-Length: 0). Cannot inscribe empty content.");
|
|
69
66
|
}
|
|
67
|
+
if (contentLength && parseInt(contentLength) < 10) {
|
|
68
|
+
throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);
|
|
69
|
+
}
|
|
70
|
+
if (!contentType || contentType === "application/octet-stream") {
|
|
71
|
+
console.log(`[InscribeFromUrlTool] Content-Type unclear, fetching first 1KB to verify...`);
|
|
72
|
+
const getController = new AbortController();
|
|
73
|
+
const getTimeoutId = setTimeout(() => getController.abort(), 5e3);
|
|
74
|
+
try {
|
|
75
|
+
const getResponse = await fetch(params.url, {
|
|
76
|
+
signal: getController.signal,
|
|
77
|
+
headers: {
|
|
78
|
+
"Range": "bytes=0-1023"
|
|
79
|
+
// Get first 1KB
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
clearTimeout(getTimeoutId);
|
|
83
|
+
if (getResponse.ok || getResponse.status === 206) {
|
|
84
|
+
const buffer = await getResponse.arrayBuffer();
|
|
85
|
+
const bytes = new Uint8Array(buffer);
|
|
86
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes.slice(0, 512));
|
|
87
|
+
if (text.toLowerCase().includes("<!doctype html") || text.toLowerCase().includes("<html") || text.match(/<meta\s+[^>]*>/i) || text.match(/<title>/i)) {
|
|
88
|
+
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.`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (getError) {
|
|
92
|
+
clearTimeout(getTimeoutId);
|
|
93
|
+
if (getError instanceof Error && getError.message.includes("HTML content")) {
|
|
94
|
+
throw getError;
|
|
95
|
+
}
|
|
96
|
+
console.log(`[InscribeFromUrlTool] Could not perform partial GET validation: ${getError instanceof Error ? getError.message : "Unknown error"}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.log(`[InscribeFromUrlTool] URL validation passed. Content-Type: ${contentType}, Content-Length: ${contentLength || "unknown"}`);
|
|
100
|
+
} catch (fetchError) {
|
|
101
|
+
clearTimeout(timeoutId);
|
|
102
|
+
throw fetchError;
|
|
70
103
|
}
|
|
71
104
|
} catch (error) {
|
|
72
105
|
if (error instanceof Error) {
|
|
73
|
-
if (error.
|
|
74
|
-
|
|
106
|
+
if (error.name === "AbortError") {
|
|
107
|
+
console.log(`[InscribeFromUrlTool] Warning: URL validation timed out after 10 seconds. Proceeding with inscription attempt.`);
|
|
108
|
+
} else if (error.message.includes("URL returned error") || error.message.includes("empty content") || error.message.includes("too small") || error.message.includes("HTML")) {
|
|
109
|
+
throw error;
|
|
110
|
+
} else {
|
|
111
|
+
console.log(`[InscribeFromUrlTool] Warning: Could not validate URL with HEAD request: ${error.message}. Proceeding with inscription attempt.`);
|
|
75
112
|
}
|
|
76
|
-
throw error;
|
|
77
113
|
}
|
|
78
|
-
throw new Error(`Failed to read file: ${error}`);
|
|
79
114
|
}
|
|
80
|
-
const base64Data = fileContent.toString("base64");
|
|
81
|
-
this.logger?.info(`Converted to base64: ${base64Data.length} characters`);
|
|
82
|
-
const fileName = path.basename(params.filePath);
|
|
83
|
-
const mimeType = this.getMimeType(fileName);
|
|
84
|
-
this.logger?.info(`File: ${fileName}, MIME type: ${mimeType}`);
|
|
85
115
|
const options = {
|
|
86
116
|
mode: params.mode,
|
|
87
117
|
metadata: params.metadata,
|
|
@@ -104,24 +134,14 @@ class InscribeFromFileTool extends BaseInscriberQueryTool {
|
|
|
104
134
|
});
|
|
105
135
|
result = await Promise.race([
|
|
106
136
|
this.inscriberBuilder.inscribe(
|
|
107
|
-
{
|
|
108
|
-
type: "buffer",
|
|
109
|
-
buffer: Buffer.from(base64Data, "base64"),
|
|
110
|
-
fileName,
|
|
111
|
-
mimeType
|
|
112
|
-
},
|
|
137
|
+
{ type: "url", url: params.url },
|
|
113
138
|
options
|
|
114
139
|
),
|
|
115
140
|
timeoutPromise
|
|
116
141
|
]);
|
|
117
142
|
} else {
|
|
118
143
|
result = await this.inscriberBuilder.inscribe(
|
|
119
|
-
{
|
|
120
|
-
type: "buffer",
|
|
121
|
-
buffer: Buffer.from(base64Data, "base64"),
|
|
122
|
-
fileName,
|
|
123
|
-
mimeType
|
|
124
|
-
},
|
|
144
|
+
{ type: "url", url: params.url },
|
|
125
145
|
options
|
|
126
146
|
);
|
|
127
147
|
}
|
|
@@ -144,35 +164,12 @@ Transaction ID: ${result.result.transactionId}
|
|
|
144
164
|
The inscription is processing and will be confirmed shortly.`;
|
|
145
165
|
}
|
|
146
166
|
} catch (error) {
|
|
147
|
-
const errorMessage = error instanceof Error ? error.message : "Failed to inscribe from
|
|
167
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to inscribe from URL";
|
|
148
168
|
throw new Error(`Inscription failed: ${errorMessage}`);
|
|
149
169
|
}
|
|
150
170
|
}
|
|
151
|
-
getMimeType(fileName) {
|
|
152
|
-
const ext = path.extname(fileName).toLowerCase();
|
|
153
|
-
const mimeTypes = {
|
|
154
|
-
".png": "image/png",
|
|
155
|
-
".jpg": "image/jpeg",
|
|
156
|
-
".jpeg": "image/jpeg",
|
|
157
|
-
".gif": "image/gif",
|
|
158
|
-
".webp": "image/webp",
|
|
159
|
-
".svg": "image/svg+xml",
|
|
160
|
-
".pdf": "application/pdf",
|
|
161
|
-
".json": "application/json",
|
|
162
|
-
".txt": "text/plain",
|
|
163
|
-
".html": "text/html",
|
|
164
|
-
".css": "text/css",
|
|
165
|
-
".js": "application/javascript",
|
|
166
|
-
".ts": "application/typescript",
|
|
167
|
-
".mp4": "video/mp4",
|
|
168
|
-
".mp3": "audio/mpeg",
|
|
169
|
-
".wav": "audio/wav",
|
|
170
|
-
".zip": "application/zip"
|
|
171
|
-
};
|
|
172
|
-
return mimeTypes[ext] || "application/octet-stream";
|
|
173
|
-
}
|
|
174
171
|
}
|
|
175
172
|
export {
|
|
176
|
-
|
|
173
|
+
InscribeFromUrlTool
|
|
177
174
|
};
|
|
178
175
|
//# sourceMappingURL=standards-agent-kit.es34.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standards-agent-kit.es34.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":"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"}
|
|
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":"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"}
|