@aiquants/html-to-markdown 0.2.5 → 0.3.0
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/README.md +10 -8
- package/dist/{core-CFnkfU5N.js → core-CouDTni-.js} +6 -6
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -3
- package/dist/main.js +1 -1
- package/dist/mcp-server-BAmy7d8r.js +210 -0
- package/dist/mcp-server-CCu0Pcm3.cjs +1 -0
- package/dist/{mcp-server-streamable-BC-Qlr-9.js → mcp-server-streamable-CVDJHcWu.js} +109 -152
- package/dist/mcp-server-streamable-mF26pg3E.cjs +1 -0
- package/dist/mcp-streamable.cjs +1 -1
- package/dist/mcp-streamable.js +1 -1
- package/dist/mcp.cjs +1 -1
- package/dist/mcp.js +1 -1
- package/dist/src/mcp-common.d.ts +107 -0
- package/dist/src/mcp-server-streamable.d.ts +10 -13
- package/dist/src/mcp-server.d.ts +139 -22
- package/dist/{version-BXLLJx3Z.js → version-BF9-U8Ef.js} +156 -9
- package/dist/version-Cg6D6H9g.cjs +1 -0
- package/mcp.json +3 -3
- package/package.json +1 -1
- package/dist/mcp-server-Bo_mCvzz.cjs +0 -1
- package/dist/mcp-server-CueTvF4M.js +0 -364
- package/dist/mcp-server-streamable-Cc4QsZQe.cjs +0 -1
- package/dist/version-DTaUKNBS.cjs +0 -1
package/README.md
CHANGED
|
@@ -136,12 +136,15 @@ npx --package=@aiquants/html-to-markdown aiq-html2md-mcp-stream
|
|
|
136
136
|
- `url` (required*): The URL of the web page to convert
|
|
137
137
|
- `html_content` (required*): HTML content as a string to convert (alternative to URL)
|
|
138
138
|
- `locale` (optional): Browser locale (`en-US` or `ja-JP`, defaults to `en-US`)
|
|
139
|
-
- `output_format` (optional): Output format (`markdown`, `html`, or `both`, defaults to `markdown`)
|
|
140
|
-
- `save_path` (optional): Full path (including filename) where to save the converted content
|
|
141
|
-
- `save_directory` (optional): Directory path where to save the converted content with auto-generated filename
|
|
142
139
|
|
|
143
140
|
*Either `url` or `html_content` is required
|
|
144
141
|
|
|
142
|
+
- **save_content_to_file**: Save text content to a file with specified path or directory
|
|
143
|
+
- `content` (required): Text content to save to file (will be saved as Markdown format)
|
|
144
|
+
- `save_path` (optional): Complete file path including filename to save content
|
|
145
|
+
- `save_directory` (optional): Directory path to save content with auto-generated filename
|
|
146
|
+
- `filename` (optional): Base filename to use when save_directory is specified (extension .md will be added automatically)
|
|
147
|
+
|
|
145
148
|
**File Saving Options:**
|
|
146
149
|
|
|
147
150
|
- **`save_path`**: Specify the complete file path including filename and extension where you want to save the content.
|
|
@@ -157,11 +160,10 @@ npx --package=@aiquants/html-to-markdown aiq-html2md-mcp-stream
|
|
|
157
160
|
|
|
158
161
|
**File Saving Behavior:**
|
|
159
162
|
|
|
160
|
-
-
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
-
|
|
164
|
-
- With `save_directory`: Files are saved as `filename.md` and `filename.html`
|
|
163
|
+
- All content is saved as Markdown format (`.md` files)
|
|
164
|
+
- The directory will be created automatically if it doesn't exist
|
|
165
|
+
- With `save_path`: File is saved to the exact specified path
|
|
166
|
+
- With `save_directory`: File is saved with auto-generated filename based on the URL or custom filename if provided
|
|
165
167
|
|
|
166
168
|
- **html_to_markdown_streamable**: Same as above but with streamable MCP support for progress updates
|
|
167
169
|
|
|
@@ -61,12 +61,12 @@ const getMessage = (locale, key2, replacements) => {
|
|
|
61
61
|
const getHtmlWithPlaywright = async (url, locale) => {
|
|
62
62
|
let browser;
|
|
63
63
|
try {
|
|
64
|
-
console.
|
|
64
|
+
console.info(getMessage(locale, "playwright_launch"));
|
|
65
65
|
browser = await chromium.launch();
|
|
66
66
|
const page = await browser.newPage({ locale });
|
|
67
|
-
console.
|
|
67
|
+
console.info(getMessage(locale, "playwright_goto", { url, locale }));
|
|
68
68
|
await page.goto(url, { waitUntil: "networkidle" });
|
|
69
|
-
console.
|
|
69
|
+
console.info(getMessage(locale, "playwright_get_content"));
|
|
70
70
|
const html2 = await page.content();
|
|
71
71
|
return html2;
|
|
72
72
|
} catch (error) {
|
|
@@ -75,7 +75,7 @@ const getHtmlWithPlaywright = async (url, locale) => {
|
|
|
75
75
|
} finally {
|
|
76
76
|
if (browser) {
|
|
77
77
|
await browser.close();
|
|
78
|
-
console.
|
|
78
|
+
console.info(getMessage(locale, "playwright_close"));
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
};
|
|
@@ -20681,7 +20681,7 @@ const handleTableCell = (_state, node2, _parent) => {
|
|
|
20681
20681
|
return { type: "tableCell", children: [{ type: "html", value }] };
|
|
20682
20682
|
};
|
|
20683
20683
|
const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
|
|
20684
|
-
console.
|
|
20684
|
+
console.info(getMessage(locale, "convert_start"));
|
|
20685
20685
|
const slugger2 = new BananaSlug();
|
|
20686
20686
|
const file = await unified().use(rehypeParse, { fragment: true }).use(rehypeRaw).use(rehypeSanitizeHtml).use(rehypeWikipediaFootnotes).use(rehypeNamedAnchors).use(rehypeSlug).use(rehypeParagraphWrapper).use(rehypeAbsoluteLinks, { baseUrl }).use(rehypeRemark, {
|
|
20687
20687
|
handlers: {
|
|
@@ -20783,7 +20783,7 @@ const convertHtmlToMarkdown = async (html2, baseUrl, locale) => {
|
|
|
20783
20783
|
}).process(html2);
|
|
20784
20784
|
const unescapedMarkdown = String(file).replace(/\\\[\^(.+?)\\?\](?!\s*:)/g, "[^$1]").replace(/\]\(\\#/g, "](#");
|
|
20785
20785
|
const correctedMarkdown = unescapedMarkdown.replace(/(\s*)- - /g, "$1- ").replace(/(\s*)\* \* /g, "$1* ");
|
|
20786
|
-
console.
|
|
20786
|
+
console.info(getMessage(locale, "convert_success"));
|
|
20787
20787
|
return correctedMarkdown;
|
|
20788
20788
|
};
|
|
20789
20789
|
const htmlToMarkdown = async (urlOrHtml, options = {}) => {
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e}),Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./core-D2dlEt1l.cjs"),t=require("./mcp-server-
|
|
1
|
+
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e}),Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./core-D2dlEt1l.cjs"),t=require("./mcp-server-CCu0Pcm3.cjs"),c=require("./mcp-server-streamable-mF26pg3E.cjs");exports.getMessage=r.getMessage,exports.htmlToMarkdown=r.htmlToMarkdown,exports.createMcpServer=t.createMcpServer,exports.runMcpServer=t.runMcpServer,exports.createStreamableMcpServer=c.createStreamableMcpServer,exports.runStreamableMcpServer=c.runStreamableMcpServer;
|
package/dist/index.js
CHANGED
|
@@ -5,9 +5,9 @@ Object.assign(global, {
|
|
|
5
5
|
window,
|
|
6
6
|
self: window
|
|
7
7
|
});
|
|
8
|
-
import { g, h } from "./core-
|
|
9
|
-
import { c, r } from "./mcp-server-
|
|
10
|
-
import { c as c2, r as r2 } from "./mcp-server-streamable-
|
|
8
|
+
import { g, h } from "./core-CouDTni-.js";
|
|
9
|
+
import { c, r } from "./mcp-server-BAmy7d8r.js";
|
|
10
|
+
import { c as c2, r as r2 } from "./mcp-server-streamable-CVDJHcWu.js";
|
|
11
11
|
export {
|
|
12
12
|
c as createMcpServer,
|
|
13
13
|
c2 as createStreamableMcpServer,
|
package/dist/main.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.assign(global, {
|
|
|
6
6
|
window,
|
|
7
7
|
self: window
|
|
8
8
|
});
|
|
9
|
-
import { g as getMessage, h as htmlToMarkdown } from "./core-
|
|
9
|
+
import { g as getMessage, h as htmlToMarkdown } from "./core-CouDTni-.js";
|
|
10
10
|
const runCli = async () => {
|
|
11
11
|
const yargsParser = (await import("yargs-parser")).default;
|
|
12
12
|
const fs = await import("node:fs");
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Window } from "happy-dom";
|
|
2
|
+
const window = new Window();
|
|
3
|
+
Object.assign(global, {
|
|
4
|
+
document: window.document,
|
|
5
|
+
window,
|
|
6
|
+
self: window
|
|
7
|
+
});
|
|
8
|
+
import { J as JSONRPCMessageSchema, g as getPackageVersion, S as Server, L as ListToolsRequestSchema, M as MCP_TOOL_SCHEMAS, C as CallToolRequestSchema, p as parseHtmlToMarkdownArgs, c as createSuccessResult, a as createErrorResult, b as parseSaveContentArgs, s as saveContentToFile } from "./version-BF9-U8Ef.js";
|
|
9
|
+
import process from "node:process";
|
|
10
|
+
import { h as htmlToMarkdown } from "./core-CouDTni-.js";
|
|
11
|
+
class ReadBuffer {
|
|
12
|
+
append(chunk) {
|
|
13
|
+
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
14
|
+
}
|
|
15
|
+
readMessage() {
|
|
16
|
+
if (!this._buffer) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const index = this._buffer.indexOf("\n");
|
|
20
|
+
if (index === -1) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
|
|
24
|
+
this._buffer = this._buffer.subarray(index + 1);
|
|
25
|
+
return deserializeMessage(line);
|
|
26
|
+
}
|
|
27
|
+
clear() {
|
|
28
|
+
this._buffer = void 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function deserializeMessage(line) {
|
|
32
|
+
return JSONRPCMessageSchema.parse(JSON.parse(line));
|
|
33
|
+
}
|
|
34
|
+
function serializeMessage(message) {
|
|
35
|
+
return JSON.stringify(message) + "\n";
|
|
36
|
+
}
|
|
37
|
+
class StdioServerTransport {
|
|
38
|
+
constructor(_stdin = process.stdin, _stdout = process.stdout) {
|
|
39
|
+
this._stdin = _stdin;
|
|
40
|
+
this._stdout = _stdout;
|
|
41
|
+
this._readBuffer = new ReadBuffer();
|
|
42
|
+
this._started = false;
|
|
43
|
+
this._ondata = (chunk) => {
|
|
44
|
+
this._readBuffer.append(chunk);
|
|
45
|
+
this.processReadBuffer();
|
|
46
|
+
};
|
|
47
|
+
this._onerror = (error) => {
|
|
48
|
+
var _a;
|
|
49
|
+
(_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Starts listening for messages on stdin.
|
|
54
|
+
*/
|
|
55
|
+
async start() {
|
|
56
|
+
if (this._started) {
|
|
57
|
+
throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
|
|
58
|
+
}
|
|
59
|
+
this._started = true;
|
|
60
|
+
this._stdin.on("data", this._ondata);
|
|
61
|
+
this._stdin.on("error", this._onerror);
|
|
62
|
+
}
|
|
63
|
+
processReadBuffer() {
|
|
64
|
+
var _a, _b;
|
|
65
|
+
while (true) {
|
|
66
|
+
try {
|
|
67
|
+
const message = this._readBuffer.readMessage();
|
|
68
|
+
if (message === null) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
(_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async close() {
|
|
78
|
+
var _a;
|
|
79
|
+
this._stdin.off("data", this._ondata);
|
|
80
|
+
this._stdin.off("error", this._onerror);
|
|
81
|
+
const remainingDataListeners = this._stdin.listenerCount("data");
|
|
82
|
+
if (remainingDataListeners === 0) {
|
|
83
|
+
this._stdin.pause();
|
|
84
|
+
}
|
|
85
|
+
this._readBuffer.clear();
|
|
86
|
+
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
87
|
+
}
|
|
88
|
+
send(message) {
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
const json = serializeMessage(message);
|
|
91
|
+
if (this._stdout.write(json)) {
|
|
92
|
+
resolve();
|
|
93
|
+
} else {
|
|
94
|
+
this._stdout.once("drain", resolve);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class HtmlToMarkdownMcpServer {
|
|
100
|
+
constructor() {
|
|
101
|
+
this.version = getPackageVersion();
|
|
102
|
+
this.server = new Server(
|
|
103
|
+
{
|
|
104
|
+
name: "@aiquants/html-to-markdown",
|
|
105
|
+
version: this.version,
|
|
106
|
+
description: "Convert web pages and HTML to Markdown with JavaScript dynamic content support and table structure preservation. Includes separate file saving tool."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
capabilities: {
|
|
110
|
+
tools: {}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
this.setupToolHandlers();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Setup tool handlers for the MCP server.
|
|
118
|
+
* MCP サーバーのツールハンドラーを設定
|
|
119
|
+
*/
|
|
120
|
+
setupToolHandlers() {
|
|
121
|
+
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
122
|
+
return {
|
|
123
|
+
tools: [
|
|
124
|
+
{
|
|
125
|
+
name: "html_to_markdown",
|
|
126
|
+
description: "Convert web pages or HTML strings to Markdown format. Handles JavaScript-rendered dynamic content and preserves table structures.",
|
|
127
|
+
inputSchema: MCP_TOOL_SCHEMAS.htmlToMarkdown
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: "save_content_to_file",
|
|
131
|
+
description: "Save text content to a file with specified path or directory. Supports automatic filename generation and format detection.",
|
|
132
|
+
inputSchema: MCP_TOOL_SCHEMAS.saveContentToFile
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
138
|
+
switch (request.params.name) {
|
|
139
|
+
case "html_to_markdown": {
|
|
140
|
+
return await this.handleHtmlToMarkdown(request.params.arguments);
|
|
141
|
+
}
|
|
142
|
+
case "save_content_to_file": {
|
|
143
|
+
return await this.handleSaveContentToFile(request.params.arguments);
|
|
144
|
+
}
|
|
145
|
+
default:
|
|
146
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Handle HTML to Markdown conversion tool call.
|
|
152
|
+
* HTML から Markdown への変換ツール呼び出しを処理
|
|
153
|
+
*/
|
|
154
|
+
async handleHtmlToMarkdown(args) {
|
|
155
|
+
try {
|
|
156
|
+
const validatedArgs = parseHtmlToMarkdownArgs(args);
|
|
157
|
+
const { url, html_content, locale = "en-US" } = validatedArgs;
|
|
158
|
+
if (url) {
|
|
159
|
+
try {
|
|
160
|
+
new URL(url);
|
|
161
|
+
} catch {
|
|
162
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const result = await htmlToMarkdown(url || "direct-html-input", {
|
|
166
|
+
locale,
|
|
167
|
+
htmlContent: html_content
|
|
168
|
+
});
|
|
169
|
+
const responseText = result.markdown;
|
|
170
|
+
return createSuccessResult(responseText);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
return createErrorResult(error);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Handle save content to file tool call.
|
|
177
|
+
* ファイル保存ツール呼び出しを処理
|
|
178
|
+
*/
|
|
179
|
+
async handleSaveContentToFile(args) {
|
|
180
|
+
try {
|
|
181
|
+
const validatedArgs = parseSaveContentArgs(args);
|
|
182
|
+
const { content, save_path, save_directory, filename } = validatedArgs;
|
|
183
|
+
const savedPath = await saveContentToFile(content, save_path, save_directory, filename);
|
|
184
|
+
const responseText = `File saved successfully to: ${savedPath}`;
|
|
185
|
+
return createSuccessResult(responseText);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return createErrorResult(error);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Start the MCP server.
|
|
192
|
+
* MCP サーバーを開始
|
|
193
|
+
*/
|
|
194
|
+
async start() {
|
|
195
|
+
const transport = new StdioServerTransport();
|
|
196
|
+
await this.server.connect(transport);
|
|
197
|
+
console.info("HTML to Markdown MCP server started");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const createMcpServer = () => {
|
|
201
|
+
return new HtmlToMarkdownMcpServer();
|
|
202
|
+
};
|
|
203
|
+
const runMcpServer = async () => {
|
|
204
|
+
const server = createMcpServer();
|
|
205
|
+
await server.start();
|
|
206
|
+
};
|
|
207
|
+
export {
|
|
208
|
+
createMcpServer as c,
|
|
209
|
+
runMcpServer as r
|
|
210
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=new(require("happy-dom").Window);Object.assign(global,{document:t.document,window:t,self:t});const e=require("./version-Cg6D6H9g.cjs"),r=require("node:process"),s=require("./core-D2dlEt1l.cjs");class a{append(t){this._buffer=this._buffer?Buffer.concat([this._buffer,t]):t}readMessage(){if(!this._buffer)return null;const t=this._buffer.indexOf("\n");if(-1===t)return null;const r=this._buffer.toString("utf8",0,t).replace(/\r$/,"");return this._buffer=this._buffer.subarray(t+1),function(t){return e.JSONRPCMessageSchema.parse(JSON.parse(t))}(r)}clear(){this._buffer=void 0}}class n{constructor(t=r.stdin,e=r.stdout){this._stdin=t,this._stdout=e,this._readBuffer=new a,this._started=!1,this._ondata=t=>{this._readBuffer.append(t),this.processReadBuffer()},this._onerror=t=>{var e;null===(e=this.onerror)||void 0===e||e.call(this,t)}}async start(){if(this._started)throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");this._started=!0,this._stdin.on("data",this._ondata),this._stdin.on("error",this._onerror)}processReadBuffer(){for(var t,e;;)try{const e=this._readBuffer.readMessage();if(null===e)break;null===(t=this.onmessage)||void 0===t||t.call(this,e)}catch(r){null===(e=this.onerror)||void 0===e||e.call(this,r)}}async close(){var t;this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror);0===this._stdin.listenerCount("data")&&this._stdin.pause(),this._readBuffer.clear(),null===(t=this.onclose)||void 0===t||t.call(this)}send(t){return new Promise(e=>{const r=function(t){return JSON.stringify(t)+"\n"}(t);this._stdout.write(r)?e():this._stdout.once("drain",e)})}}class o{constructor(){this.version=e.getPackageVersion(),this.server=new e.Server({name:"@aiquants/html-to-markdown",version:this.version,description:"Convert web pages and HTML to Markdown with JavaScript dynamic content support and table structure preservation. Includes separate file saving tool."},{capabilities:{tools:{}}}),this.setupToolHandlers()}setupToolHandlers(){this.server.setRequestHandler(e.ListToolsRequestSchema,async()=>({tools:[{name:"html_to_markdown",description:"Convert web pages or HTML strings to Markdown format. Handles JavaScript-rendered dynamic content and preserves table structures.",inputSchema:e.MCP_TOOL_SCHEMAS.htmlToMarkdown},{name:"save_content_to_file",description:"Save text content to a file with specified path or directory. Supports automatic filename generation and format detection.",inputSchema:e.MCP_TOOL_SCHEMAS.saveContentToFile}]})),this.server.setRequestHandler(e.CallToolRequestSchema,async t=>{switch(t.params.name){case"html_to_markdown":return await this.handleHtmlToMarkdown(t.params.arguments);case"save_content_to_file":return await this.handleSaveContentToFile(t.params.arguments);default:throw new Error(`Unknown tool: ${t.params.name}`)}})}async handleHtmlToMarkdown(t){try{const r=e.parseHtmlToMarkdownArgs(t),{url:a,html_content:n,locale:o="en-US"}=r;if(a)try{new URL(a)}catch{throw new Error(`Invalid URL: ${a}`)}const i=(await s.htmlToMarkdown(a||"direct-html-input",{locale:o,htmlContent:n})).markdown;return e.createSuccessResult(i)}catch(r){return e.createErrorResult(r)}}async handleSaveContentToFile(t){try{const r=e.parseSaveContentArgs(t),{content:s,save_path:a,save_directory:n,filename:o}=r,i=`File saved successfully to: ${await e.saveContentToFile(s,a,n,o)}`;return e.createSuccessResult(i)}catch(r){return e.createErrorResult(r)}}async start(){const t=new n;await this.server.connect(t)}}const i=()=>new o;exports.createMcpServer=i,exports.runMcpServer=async()=>{const t=i();await t.start()};
|