@hashgraphonline/standards-agent-kit 0.2.109 → 0.2.112
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/builders/hcs6/hcs6-builder.d.ts +47 -0
- package/dist/cjs/builders/hcs6/index.d.ts +1 -0
- package/dist/cjs/builders/index.d.ts +1 -0
- package/dist/cjs/standards-agent-kit.cjs +1 -1
- package/dist/cjs/standards-agent-kit.cjs.map +1 -1
- package/dist/es/builders/hcs6/hcs6-builder.d.ts +47 -0
- package/dist/es/builders/hcs6/index.d.ts +1 -0
- package/dist/es/builders/index.d.ts +1 -0
- package/dist/es/standards-agent-kit.es27.js +53 -1
- package/dist/es/standards-agent-kit.es27.js.map +1 -1
- package/dist/es/standards-agent-kit.es28.js +27 -4
- package/dist/es/standards-agent-kit.es28.js.map +1 -1
- package/dist/es/standards-agent-kit.es29.js +30 -6
- package/dist/es/standards-agent-kit.es29.js.map +1 -1
- package/dist/umd/builders/hcs6/hcs6-builder.d.ts +47 -0
- package/dist/umd/builders/hcs6/index.d.ts +1 -0
- package/dist/umd/builders/index.d.ts +1 -0
- package/dist/umd/standards-agent-kit.umd.js +1 -1
- package/dist/umd/standards-agent-kit.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/builders/hcs6/hcs6-builder.ts +136 -0
- package/src/builders/hcs6/index.ts +1 -0
- package/src/builders/index.ts +1 -0
- package/src/tools/inscriber/InscribeFromBufferTool.ts +35 -6
- package/src/tools/inscriber/InscribeFromFileTool.ts +31 -4
- package/src/tools/inscriber/InscribeFromUrlTool.ts +63 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hashgraphonline/standards-agent-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.112",
|
|
4
4
|
"description": "A modular SDK for building on-chain autonomous agents using Hashgraph Online Standards, including HCS-10 for agent discovery and communication.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/standards-agent-kit.cjs",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
},
|
|
90
90
|
"dependencies": {
|
|
91
91
|
"@hashgraph/sdk": "^2.69.0",
|
|
92
|
-
"@hashgraphonline/standards-sdk": "0.0.
|
|
92
|
+
"@hashgraphonline/standards-sdk": "0.0.167",
|
|
93
93
|
"@langchain/community": "^0.3.49",
|
|
94
94
|
"@langchain/core": "^0.3.66",
|
|
95
95
|
"@langchain/openai": "^0.6.3",
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { BaseServiceBuilder } from 'hedera-agent-kit';
|
|
2
|
+
import type { HederaAgentKit } from 'hedera-agent-kit';
|
|
3
|
+
import {
|
|
4
|
+
HCS6Client,
|
|
5
|
+
SDKHCS6ClientConfig,
|
|
6
|
+
HCS6CreateRegistryOptions,
|
|
7
|
+
HCS6RegisterEntryOptions,
|
|
8
|
+
HCS6QueryRegistryOptions,
|
|
9
|
+
HCS6RegisterOptions,
|
|
10
|
+
HCS6CreateHashinalOptions,
|
|
11
|
+
HCS6TopicRegistrationResponse,
|
|
12
|
+
HCS6RegistryOperationResponse,
|
|
13
|
+
HCS6TopicRegistry,
|
|
14
|
+
HCS6CreateHashinalResponse,
|
|
15
|
+
NetworkType,
|
|
16
|
+
} from '@hashgraphonline/standards-sdk';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Builder for HCS-6 operations that delegates to HCS6Client
|
|
20
|
+
*/
|
|
21
|
+
export class HCS6Builder extends BaseServiceBuilder {
|
|
22
|
+
protected hcs6Client?: HCS6Client;
|
|
23
|
+
|
|
24
|
+
constructor(hederaKit: HederaAgentKit) {
|
|
25
|
+
super(hederaKit);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get or create HCS-6 client
|
|
30
|
+
*/
|
|
31
|
+
protected async getHCS6Client(): Promise<HCS6Client> {
|
|
32
|
+
if (!this.hcs6Client) {
|
|
33
|
+
const operatorId = this.hederaKit.signer.getAccountId().toString();
|
|
34
|
+
const operatorPrivateKey = this.hederaKit.signer?.getOperatorPrivateKey()
|
|
35
|
+
? this.hederaKit.signer.getOperatorPrivateKey().toStringRaw()
|
|
36
|
+
: '';
|
|
37
|
+
|
|
38
|
+
const network = this.hederaKit.client.network;
|
|
39
|
+
const networkType: NetworkType = network.toString().includes('mainnet')
|
|
40
|
+
? 'mainnet'
|
|
41
|
+
: 'testnet';
|
|
42
|
+
|
|
43
|
+
const config: SDKHCS6ClientConfig = {
|
|
44
|
+
network: networkType,
|
|
45
|
+
operatorId: operatorId,
|
|
46
|
+
operatorKey: operatorPrivateKey,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
this.hcs6Client = new HCS6Client(config);
|
|
50
|
+
}
|
|
51
|
+
return this.hcs6Client;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a new HCS-6 dynamic registry
|
|
56
|
+
* Note: This executes the transaction directly via HCS6Client
|
|
57
|
+
*/
|
|
58
|
+
async createRegistry(
|
|
59
|
+
options: HCS6CreateRegistryOptions = {}
|
|
60
|
+
): Promise<HCS6TopicRegistrationResponse> {
|
|
61
|
+
const client = await this.getHCS6Client();
|
|
62
|
+
return await client.createRegistry(options);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Register a new dynamic hashinal entry in an HCS-6 registry
|
|
67
|
+
*/
|
|
68
|
+
async registerEntry(
|
|
69
|
+
registryTopicId: string,
|
|
70
|
+
options: HCS6RegisterEntryOptions
|
|
71
|
+
): Promise<HCS6RegistryOperationResponse> {
|
|
72
|
+
const client = await this.getHCS6Client();
|
|
73
|
+
return await client.registerEntry(registryTopicId, options);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Query entries from an HCS-6 registry
|
|
78
|
+
*/
|
|
79
|
+
async getRegistry(
|
|
80
|
+
topicId: string,
|
|
81
|
+
options: HCS6QueryRegistryOptions = {}
|
|
82
|
+
): Promise<HCS6TopicRegistry> {
|
|
83
|
+
const client = await this.getHCS6Client();
|
|
84
|
+
return await client.getRegistry(topicId, options);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a complete dynamic hashinal with inscription and registry
|
|
89
|
+
*/
|
|
90
|
+
async createHashinal(
|
|
91
|
+
options: HCS6CreateHashinalOptions
|
|
92
|
+
): Promise<HCS6CreateHashinalResponse> {
|
|
93
|
+
const client = await this.getHCS6Client();
|
|
94
|
+
return await client.createHashinal(options);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Register a dynamic hashinal with combined inscription and registry creation
|
|
99
|
+
* This is the main method for creating and updating dynamic hashinals
|
|
100
|
+
*/
|
|
101
|
+
async register(
|
|
102
|
+
options: HCS6RegisterOptions
|
|
103
|
+
): Promise<HCS6CreateHashinalResponse> {
|
|
104
|
+
const client = await this.getHCS6Client();
|
|
105
|
+
return await client.register(options);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Submit a raw message to an HCS-6 topic
|
|
110
|
+
*/
|
|
111
|
+
async submitMessage(
|
|
112
|
+
topicId: string,
|
|
113
|
+
payload: any
|
|
114
|
+
): Promise<any> {
|
|
115
|
+
const client = await this.getHCS6Client();
|
|
116
|
+
return await client.submitMessage(topicId, payload);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get topic info from mirror node
|
|
121
|
+
*/
|
|
122
|
+
async getTopicInfo(topicId: string): Promise<any> {
|
|
123
|
+
const client = await this.getHCS6Client();
|
|
124
|
+
return await client.getTopicInfo(topicId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Close the HCS-6 client
|
|
129
|
+
*/
|
|
130
|
+
async close(): Promise<void> {
|
|
131
|
+
if (this.hcs6Client) {
|
|
132
|
+
this.hcs6Client.close();
|
|
133
|
+
this.hcs6Client = undefined;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './hcs6-builder';
|
package/src/builders/index.ts
CHANGED
|
@@ -7,8 +7,8 @@ import { CallbackManagerForToolRun } from '@langchain/core/callbacks/manager';
|
|
|
7
7
|
* Schema for inscribing from buffer
|
|
8
8
|
*/
|
|
9
9
|
const inscribeFromBufferSchema = z.object({
|
|
10
|
-
base64Data: z.string().describe('Base64 encoded content to inscribe'),
|
|
11
|
-
fileName: z.string().describe('Name for the inscribed content'),
|
|
10
|
+
base64Data: z.string().min(1, 'Base64 data cannot be empty').describe('Base64 encoded content to inscribe. Must contain valid, non-empty content (minimum 10 bytes after decoding).'),
|
|
11
|
+
fileName: z.string().min(1, 'File name cannot be empty').describe('Name for the inscribed content. Required for all inscriptions.'),
|
|
12
12
|
mimeType: z
|
|
13
13
|
.string()
|
|
14
14
|
.optional()
|
|
@@ -53,7 +53,7 @@ const inscribeFromBufferSchema = z.object({
|
|
|
53
53
|
*/
|
|
54
54
|
export class InscribeFromBufferTool extends BaseInscriberQueryTool<typeof inscribeFromBufferSchema> {
|
|
55
55
|
name = 'inscribeFromBuffer';
|
|
56
|
-
description = 'Inscribe content
|
|
56
|
+
description = 'Inscribe content that you already have (text, data, or files) to the Hedera network. Use this tool when you have content from: Wikipedia articles, MCP tool responses, text data, API responses, or any content you\'ve retrieved. Convert your content to base64 first. DO NOT use inscribeFromUrl for web page content - use this tool instead after retrieving the actual content.';
|
|
57
57
|
|
|
58
58
|
get specificInputSchema() {
|
|
59
59
|
return inscribeFromBufferSchema;
|
|
@@ -70,15 +70,44 @@ export class InscribeFromBufferTool extends BaseInscriberQueryTool<typeof inscri
|
|
|
70
70
|
|
|
71
71
|
if (!params.base64Data || params.base64Data.trim() === '') {
|
|
72
72
|
console.log(`[InscribeFromBufferTool] ERROR: No data provided`);
|
|
73
|
-
throw new Error('No data provided. Cannot inscribe empty content.');
|
|
73
|
+
throw new Error('No data provided. Cannot inscribe empty content. Please provide valid base64 encoded data.');
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
if (!params.fileName || params.fileName.trim() === '') {
|
|
77
|
+
console.log(`[InscribeFromBufferTool] ERROR: No fileName provided`);
|
|
78
|
+
throw new Error('No fileName provided. A valid fileName is required for inscription.');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let buffer: Buffer;
|
|
82
|
+
try {
|
|
83
|
+
buffer = Buffer.from(params.base64Data, 'base64');
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.log(`[InscribeFromBufferTool] ERROR: Invalid base64 data`);
|
|
86
|
+
throw new Error('Invalid base64 data provided. Please ensure the data is properly base64 encoded.');
|
|
87
|
+
}
|
|
88
|
+
|
|
77
89
|
console.log(`[InscribeFromBufferTool] Buffer length after conversion: ${buffer.length}`);
|
|
78
90
|
|
|
79
91
|
if (buffer.length === 0) {
|
|
80
92
|
console.log(`[InscribeFromBufferTool] ERROR: Buffer is empty after conversion`);
|
|
81
|
-
throw new Error('Buffer is empty.
|
|
93
|
+
throw new Error('Buffer is empty after base64 conversion. The provided data appears to be invalid or empty.');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (buffer.length < 10) {
|
|
97
|
+
console.log(`[InscribeFromBufferTool] WARNING: Buffer is very small (${buffer.length} bytes)`);
|
|
98
|
+
console.log(`[InscribeFromBufferTool] Buffer content preview: ${buffer.toString('utf8', 0, Math.min(buffer.length, 50))}`);
|
|
99
|
+
throw new Error(`Buffer content is too small (${buffer.length} bytes). This may indicate empty or invalid content. Please verify the source data contains actual content.`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const isValidBase64 = /^[A-Za-z0-9+/]*={0,2}$/.test(params.base64Data);
|
|
103
|
+
if (!isValidBase64) {
|
|
104
|
+
console.log(`[InscribeFromBufferTool] ERROR: Invalid base64 format`);
|
|
105
|
+
throw new Error('Invalid base64 format. The data does not appear to be properly base64 encoded.');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (buffer.toString('utf8', 0, Math.min(buffer.length, 100)).trim() === '') {
|
|
109
|
+
console.log(`[InscribeFromBufferTool] ERROR: Buffer contains only whitespace or empty content`);
|
|
110
|
+
throw new Error('Buffer contains only whitespace or empty content. Cannot inscribe meaningless data.');
|
|
82
111
|
}
|
|
83
112
|
|
|
84
113
|
const options: InscriptionOptions = {
|
|
@@ -9,7 +9,7 @@ import * as path from 'path';
|
|
|
9
9
|
* Schema for inscribing from file
|
|
10
10
|
*/
|
|
11
11
|
const inscribeFromFileSchema = z.object({
|
|
12
|
-
filePath: z.string().describe('The file path of the content to inscribe'),
|
|
12
|
+
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.'),
|
|
13
13
|
mode: z
|
|
14
14
|
.enum(['file', 'hashinal'])
|
|
15
15
|
.optional()
|
|
@@ -49,7 +49,7 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
49
49
|
> {
|
|
50
50
|
name = 'inscribeFromFile';
|
|
51
51
|
description =
|
|
52
|
-
'Inscribe content from a local file to the Hedera network using a file path. For files accessed through MCP filesystem tools, consider reading the file content first and using inscribeFromBuffer instead.';
|
|
52
|
+
'Inscribe content from a local file to the Hedera network using a file path. IMPORTANT: Only use this tool when you have a valid file path to actual content. The file must exist and contain meaningful data (minimum 10 bytes). For files accessed through MCP filesystem tools, consider reading the file content first and using inscribeFromBuffer instead.';
|
|
53
53
|
|
|
54
54
|
get specificInputSchema(): typeof inscribeFromFileSchema {
|
|
55
55
|
return inscribeFromFileSchema;
|
|
@@ -76,19 +76,46 @@ export class InscribeFromFileTool extends BaseInscriberQueryTool<
|
|
|
76
76
|
|
|
77
77
|
if (stats.size === 0) {
|
|
78
78
|
throw new Error(
|
|
79
|
-
`File "${params.filePath}" is empty. Cannot inscribe empty files.`
|
|
79
|
+
`File "${params.filePath}" is empty (0 bytes). Cannot inscribe empty files.`
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
if (stats.size < 10) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`File "${params.filePath}" is too small (${stats.size} bytes). Files must contain at least 10 bytes of meaningful content.`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (stats.size > 100 * 1024 * 1024) {
|
|
90
|
+
console.log(`[InscribeFromFileTool] WARNING: Large file detected (${(stats.size / (1024 * 1024)).toFixed(2)} MB)`);
|
|
91
|
+
}
|
|
92
|
+
|
|
83
93
|
this.logger?.info('Reading file content...');
|
|
84
94
|
fileContent = await fs.readFile(params.filePath);
|
|
85
95
|
this.logger?.info(`Read ${fileContent.length} bytes from file`);
|
|
86
96
|
|
|
87
97
|
if (!fileContent || fileContent.length === 0) {
|
|
88
98
|
throw new Error(
|
|
89
|
-
`File "${params.filePath}" has no content. Cannot inscribe empty files.`
|
|
99
|
+
`File "${params.filePath}" has no content after reading. Cannot inscribe empty files.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (fileContent.length < 10) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`File "${params.filePath}" content is too small (${fileContent.length} bytes). Files must contain at least 10 bytes of meaningful content.`
|
|
90
106
|
);
|
|
91
107
|
}
|
|
108
|
+
|
|
109
|
+
const fileName = path.basename(params.filePath);
|
|
110
|
+
const mimeType = this.getMimeType(fileName);
|
|
111
|
+
if (mimeType.startsWith('text/') || mimeType === 'application/json') {
|
|
112
|
+
const textContent = fileContent.toString('utf8', 0, Math.min(fileContent.length, 1000));
|
|
113
|
+
if (textContent.trim() === '') {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`File "${params.filePath}" contains only whitespace or empty content. Cannot inscribe meaningless data.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
92
119
|
} catch (error) {
|
|
93
120
|
if (error instanceof Error) {
|
|
94
121
|
if (error.message.includes('ENOENT')) {
|
|
@@ -42,7 +42,7 @@ const inscribeFromUrlSchema = z.object({
|
|
|
42
42
|
*/
|
|
43
43
|
export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeFromUrlSchema> {
|
|
44
44
|
name = 'inscribeFromUrl';
|
|
45
|
-
description = 'Inscribe content from a URL to
|
|
45
|
+
description = 'Inscribe content directly from a URL that points to a downloadable file (PDF, image, JSON, etc). DO NOT use this for web pages like Wikipedia, Twitter, or GitHub pages. This tool is ONLY for direct file URLs like https://example.com/document.pdf or https://api.example.com/data.json. For content you already have (from Wikipedia, MCP tools, or text), use inscribeFromBuffer instead.';
|
|
46
46
|
|
|
47
47
|
get specificInputSchema() {
|
|
48
48
|
return inscribeFromUrlSchema;
|
|
@@ -52,6 +52,68 @@ export class InscribeFromUrlTool extends BaseInscriberQueryTool<typeof inscribeF
|
|
|
52
52
|
params: z.infer<typeof inscribeFromUrlSchema>,
|
|
53
53
|
_runManager?: CallbackManagerForToolRun
|
|
54
54
|
): Promise<unknown> {
|
|
55
|
+
console.log(`[DEBUG] InscribeFromUrlTool.executeQuery called with URL: ${params.url}`);
|
|
56
|
+
|
|
57
|
+
if (!params.url || params.url.trim() === '') {
|
|
58
|
+
throw new Error('URL cannot be empty. Please provide a valid URL.');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const urlObj = new URL(params.url);
|
|
63
|
+
if (!urlObj.protocol || !urlObj.host) {
|
|
64
|
+
throw new Error('Invalid URL format. Please provide a complete URL with protocol (http/https).');
|
|
65
|
+
}
|
|
66
|
+
if (!['http:', 'https:'].includes(urlObj.protocol)) {
|
|
67
|
+
throw new Error('Only HTTP and HTTPS URLs are supported for inscription.');
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new Error(`Invalid URL: ${params.url}. Please provide a valid URL.`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(`[InscribeFromUrlTool] Validating URL content before inscription...`);
|
|
74
|
+
try {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const timeoutId = setTimeout(() => controller.abort(), 10000);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(params.url, {
|
|
80
|
+
method: 'HEAD',
|
|
81
|
+
signal: controller.signal,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
clearTimeout(timeoutId);
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
throw new Error(`URL returned error status ${response.status}: ${response.statusText}. Cannot inscribe content from inaccessible URLs.`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const contentLength = response.headers.get('content-length');
|
|
91
|
+
if (contentLength && parseInt(contentLength) === 0) {
|
|
92
|
+
throw new Error('URL returns empty content (Content-Length: 0). Cannot inscribe empty content.');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (contentLength && parseInt(contentLength) < 10) {
|
|
96
|
+
throw new Error(`URL content is too small (${contentLength} bytes). Content must be at least 10 bytes.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const contentType = response.headers.get('content-type');
|
|
100
|
+
console.log(`[InscribeFromUrlTool] URL validation passed. Content-Type: ${contentType}, Content-Length: ${contentLength || 'unknown'}`);
|
|
101
|
+
} catch (fetchError) {
|
|
102
|
+
clearTimeout(timeoutId);
|
|
103
|
+
throw fetchError;
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof Error) {
|
|
107
|
+
if (error.name === 'AbortError') {
|
|
108
|
+
console.log(`[InscribeFromUrlTool] Warning: URL validation timed out after 10 seconds. Proceeding with inscription attempt.`);
|
|
109
|
+
} else if (error.message.includes('URL returned error') || error.message.includes('empty content') || error.message.includes('too small')) {
|
|
110
|
+
throw error;
|
|
111
|
+
} else {
|
|
112
|
+
console.log(`[InscribeFromUrlTool] Warning: Could not validate URL with HEAD request: ${error.message}. Proceeding with inscription attempt.`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
55
117
|
const options: InscriptionOptions = {
|
|
56
118
|
mode: params.mode,
|
|
57
119
|
metadata: params.metadata,
|