@synergenius/flow-weaver 0.33.0 → 0.33.1
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/cli/flow-weaver.mjs
CHANGED
|
@@ -5987,7 +5987,7 @@ var VERSION;
|
|
|
5987
5987
|
var init_generated_version = __esm({
|
|
5988
5988
|
"src/generated-version.ts"() {
|
|
5989
5989
|
"use strict";
|
|
5990
|
-
VERSION = "0.33.
|
|
5990
|
+
VERSION = "0.33.1";
|
|
5991
5991
|
}
|
|
5992
5992
|
});
|
|
5993
5993
|
|
|
@@ -84248,6 +84248,169 @@ var init_tools_debug = __esm({
|
|
|
84248
84248
|
}
|
|
84249
84249
|
});
|
|
84250
84250
|
|
|
84251
|
+
// src/mcp/run-registry.ts
|
|
84252
|
+
function storePendingRun(run) {
|
|
84253
|
+
pendingRuns.set(run.runId, run);
|
|
84254
|
+
}
|
|
84255
|
+
function getPendingRun(runId) {
|
|
84256
|
+
return pendingRuns.get(runId);
|
|
84257
|
+
}
|
|
84258
|
+
function removePendingRun(runId) {
|
|
84259
|
+
pendingRuns.delete(runId);
|
|
84260
|
+
}
|
|
84261
|
+
var pendingRuns;
|
|
84262
|
+
var init_run_registry = __esm({
|
|
84263
|
+
"src/mcp/run-registry.ts"() {
|
|
84264
|
+
"use strict";
|
|
84265
|
+
pendingRuns = /* @__PURE__ */ new Map();
|
|
84266
|
+
}
|
|
84267
|
+
});
|
|
84268
|
+
|
|
84269
|
+
// src/mcp/tools-workflow-run.ts
|
|
84270
|
+
function makeToolResult2(data) {
|
|
84271
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
84272
|
+
}
|
|
84273
|
+
function makeErrorResult2(code, message) {
|
|
84274
|
+
return {
|
|
84275
|
+
content: [{ type: "text", text: JSON.stringify({ error: code, message }) }],
|
|
84276
|
+
isError: true
|
|
84277
|
+
};
|
|
84278
|
+
}
|
|
84279
|
+
function generateRunId() {
|
|
84280
|
+
return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
84281
|
+
}
|
|
84282
|
+
async function raceAgentPause(executionPromise, agentChannel) {
|
|
84283
|
+
try {
|
|
84284
|
+
const outcome = await Promise.race([
|
|
84285
|
+
executionPromise.then((r) => ({
|
|
84286
|
+
type: "completed",
|
|
84287
|
+
result: r?.result ?? r
|
|
84288
|
+
})),
|
|
84289
|
+
agentChannel.onPause().then((request) => ({
|
|
84290
|
+
type: "waiting_for_agent",
|
|
84291
|
+
request
|
|
84292
|
+
}))
|
|
84293
|
+
]);
|
|
84294
|
+
return outcome;
|
|
84295
|
+
} catch (err) {
|
|
84296
|
+
return {
|
|
84297
|
+
type: "error",
|
|
84298
|
+
message: err instanceof Error ? err.message : String(err)
|
|
84299
|
+
};
|
|
84300
|
+
}
|
|
84301
|
+
}
|
|
84302
|
+
async function runWorkflowWithAgent(filePath, params, workflowName) {
|
|
84303
|
+
const agentChannel = new AgentChannel();
|
|
84304
|
+
const runId = generateRunId();
|
|
84305
|
+
const executionPromise = executeWorkflowFromFile(
|
|
84306
|
+
filePath,
|
|
84307
|
+
params,
|
|
84308
|
+
{
|
|
84309
|
+
workflowName,
|
|
84310
|
+
agentChannel,
|
|
84311
|
+
includeTrace: true
|
|
84312
|
+
}
|
|
84313
|
+
);
|
|
84314
|
+
const outcome = await raceAgentPause(executionPromise, agentChannel);
|
|
84315
|
+
if (outcome.type === "completed") {
|
|
84316
|
+
return { status: "completed", result: outcome.result };
|
|
84317
|
+
}
|
|
84318
|
+
if (outcome.type === "waiting_for_agent") {
|
|
84319
|
+
storePendingRun({
|
|
84320
|
+
runId,
|
|
84321
|
+
filePath,
|
|
84322
|
+
workflowName,
|
|
84323
|
+
executionPromise,
|
|
84324
|
+
agentChannel,
|
|
84325
|
+
request: outcome.request,
|
|
84326
|
+
createdAt: Date.now(),
|
|
84327
|
+
tmpFiles: []
|
|
84328
|
+
});
|
|
84329
|
+
return { status: "waiting_for_agent", runId, agentRequest: outcome.request, agentChannel };
|
|
84330
|
+
}
|
|
84331
|
+
return { status: "error", message: outcome.message };
|
|
84332
|
+
}
|
|
84333
|
+
async function resumeWorkflow(runId, agentResult) {
|
|
84334
|
+
const pendingRun = getPendingRun(runId);
|
|
84335
|
+
if (!pendingRun) {
|
|
84336
|
+
return { status: "error", message: `No pending run found with ID "${runId}".` };
|
|
84337
|
+
}
|
|
84338
|
+
pendingRun.agentChannel.resume(agentResult);
|
|
84339
|
+
const outcome = await raceAgentPause(pendingRun.executionPromise, pendingRun.agentChannel);
|
|
84340
|
+
if (outcome.type === "completed") {
|
|
84341
|
+
removePendingRun(runId);
|
|
84342
|
+
return { status: "completed", result: outcome.result };
|
|
84343
|
+
}
|
|
84344
|
+
if (outcome.type === "waiting_for_agent") {
|
|
84345
|
+
pendingRun.request = outcome.request;
|
|
84346
|
+
return { status: "waiting_for_agent", runId, agentRequest: outcome.request, agentChannel: pendingRun.agentChannel };
|
|
84347
|
+
}
|
|
84348
|
+
removePendingRun(runId);
|
|
84349
|
+
return { status: "error", message: outcome.message };
|
|
84350
|
+
}
|
|
84351
|
+
function registerWorkflowRunTools(mcp) {
|
|
84352
|
+
mcp.tool(
|
|
84353
|
+
"fw_workflow_run",
|
|
84354
|
+
"Run a workflow. If it hits a waitForAgent node, returns the agent request so you can respond. If it completes without pausing, returns the result directly.",
|
|
84355
|
+
{
|
|
84356
|
+
filePath: external_exports.string().describe("Path to the workflow .ts file"),
|
|
84357
|
+
params: external_exports.record(external_exports.unknown()).optional().describe("Input parameters for the workflow"),
|
|
84358
|
+
workflowName: external_exports.string().optional().describe("Specific workflow export name (for multi-workflow files)")
|
|
84359
|
+
},
|
|
84360
|
+
async (args) => {
|
|
84361
|
+
try {
|
|
84362
|
+
const result = await runWorkflowWithAgent(
|
|
84363
|
+
args.filePath,
|
|
84364
|
+
args.params ?? {},
|
|
84365
|
+
args.workflowName
|
|
84366
|
+
);
|
|
84367
|
+
if (result.status === "error") {
|
|
84368
|
+
return makeErrorResult2("EXECUTION_ERROR", result.message);
|
|
84369
|
+
}
|
|
84370
|
+
const { agentChannel: _ac, ...output } = result;
|
|
84371
|
+
return makeToolResult2(output);
|
|
84372
|
+
} catch (err) {
|
|
84373
|
+
return makeErrorResult2(
|
|
84374
|
+
"EXECUTION_ERROR",
|
|
84375
|
+
err instanceof Error ? err.message : String(err)
|
|
84376
|
+
);
|
|
84377
|
+
}
|
|
84378
|
+
}
|
|
84379
|
+
);
|
|
84380
|
+
mcp.tool(
|
|
84381
|
+
"fw_workflow_resume",
|
|
84382
|
+
"Resume a workflow that paused at waitForAgent. Provide the agent result to continue execution.",
|
|
84383
|
+
{
|
|
84384
|
+
runId: external_exports.string().describe("Run ID from fw_workflow_run"),
|
|
84385
|
+
agentResult: external_exports.record(external_exports.unknown()).describe("The agent result to pass back to the workflow")
|
|
84386
|
+
},
|
|
84387
|
+
async (args) => {
|
|
84388
|
+
try {
|
|
84389
|
+
const result = await resumeWorkflow(args.runId, args.agentResult);
|
|
84390
|
+
if (result.status === "error") {
|
|
84391
|
+
return makeErrorResult2("RESUME_ERROR", result.message);
|
|
84392
|
+
}
|
|
84393
|
+
const { agentChannel: _ac, ...output } = result;
|
|
84394
|
+
return makeToolResult2(output);
|
|
84395
|
+
} catch (err) {
|
|
84396
|
+
return makeErrorResult2(
|
|
84397
|
+
"RESUME_ERROR",
|
|
84398
|
+
err instanceof Error ? err.message : String(err)
|
|
84399
|
+
);
|
|
84400
|
+
}
|
|
84401
|
+
}
|
|
84402
|
+
);
|
|
84403
|
+
}
|
|
84404
|
+
var init_tools_workflow_run = __esm({
|
|
84405
|
+
"src/mcp/tools-workflow-run.ts"() {
|
|
84406
|
+
"use strict";
|
|
84407
|
+
init_zod();
|
|
84408
|
+
init_workflow_executor();
|
|
84409
|
+
init_agent_channel();
|
|
84410
|
+
init_run_registry();
|
|
84411
|
+
}
|
|
84412
|
+
});
|
|
84413
|
+
|
|
84251
84414
|
// src/mcp/tools-context.ts
|
|
84252
84415
|
function registerContextTools(mcp) {
|
|
84253
84416
|
mcp.tool(
|
|
@@ -84492,6 +84655,7 @@ async function startMcpServer(options) {
|
|
|
84492
84655
|
registerDocsTools(mcp);
|
|
84493
84656
|
registerModelTools(mcp);
|
|
84494
84657
|
registerDebugTools(mcp);
|
|
84658
|
+
registerWorkflowRunTools(mcp);
|
|
84495
84659
|
registerContextTools(mcp);
|
|
84496
84660
|
registerResourceTools(mcp);
|
|
84497
84661
|
registerPrompts(mcp);
|
|
@@ -84528,6 +84692,7 @@ var init_server3 = __esm({
|
|
|
84528
84692
|
init_tools_docs();
|
|
84529
84693
|
init_tools_model();
|
|
84530
84694
|
init_tools_debug();
|
|
84695
|
+
init_tools_workflow_run();
|
|
84531
84696
|
init_tools_context();
|
|
84532
84697
|
init_tools_resources();
|
|
84533
84698
|
init_prompts();
|
|
@@ -89091,7 +89256,7 @@ function parseIntStrict(value) {
|
|
|
89091
89256
|
// src/cli/index.ts
|
|
89092
89257
|
init_logger();
|
|
89093
89258
|
init_error_utils();
|
|
89094
|
-
var version2 = true ? "0.33.
|
|
89259
|
+
var version2 = true ? "0.33.1" : "0.0.0-dev";
|
|
89095
89260
|
var program2 = new Command();
|
|
89096
89261
|
program2.name("fw").description("Flow Weaver Annotations - Compile and validate workflow files").option("-v, --version", "Output the current version").option("--no-color", "Disable colors").option("--color", "Force colors").on("option:version", () => {
|
|
89097
89262
|
logger.banner(version2);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.33.
|
|
1
|
+
export declare const VERSION = "0.33.1";
|
|
2
2
|
//# sourceMappingURL=generated-version.d.ts.map
|
package/dist/mcp/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import { registerDiagramTools } from './tools-diagram.js';
|
|
|
11
11
|
import { registerDocsTools } from './tools-docs.js';
|
|
12
12
|
import { registerModelTools } from './tools-model.js';
|
|
13
13
|
import { registerDebugTools } from './tools-debug.js';
|
|
14
|
+
import { registerWorkflowRunTools } from './tools-workflow-run.js';
|
|
14
15
|
import { registerContextTools } from './tools-context.js';
|
|
15
16
|
import { registerResourceTools } from './tools-resources.js';
|
|
16
17
|
import { registerPrompts } from './prompts.js';
|
|
@@ -31,6 +32,7 @@ export async function startMcpServer(options) {
|
|
|
31
32
|
registerDocsTools(mcp);
|
|
32
33
|
registerModelTools(mcp);
|
|
33
34
|
registerDebugTools(mcp);
|
|
35
|
+
registerWorkflowRunTools(mcp);
|
|
34
36
|
registerContextTools(mcp);
|
|
35
37
|
registerResourceTools(mcp);
|
|
36
38
|
registerPrompts(mcp);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools for running workflows with waitForAgent pause/resume support.
|
|
3
|
+
*
|
|
4
|
+
* fw_workflow_run: Starts a workflow. If it hits waitForAgent, returns the
|
|
5
|
+
* agent request to the MCP caller (e.g. Claude Code) with status "waiting_for_agent".
|
|
6
|
+
* If the workflow completes without pausing, returns the result directly.
|
|
7
|
+
*
|
|
8
|
+
* fw_workflow_resume: Resumes a paused workflow with the agent's result.
|
|
9
|
+
* Returns the final workflow result (or pauses again if another waitForAgent is hit).
|
|
10
|
+
*/
|
|
11
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
|
+
import { AgentChannel } from './agent-channel.js';
|
|
13
|
+
/** Exposed for testing — run workflow with pause/resume support */
|
|
14
|
+
export declare function runWorkflowWithAgent(filePath: string, params: Record<string, unknown>, workflowName?: string): Promise<{
|
|
15
|
+
status: 'completed';
|
|
16
|
+
result: unknown;
|
|
17
|
+
} | {
|
|
18
|
+
status: 'waiting_for_agent';
|
|
19
|
+
runId: string;
|
|
20
|
+
agentRequest: object;
|
|
21
|
+
agentChannel: AgentChannel;
|
|
22
|
+
} | {
|
|
23
|
+
status: 'error';
|
|
24
|
+
message: string;
|
|
25
|
+
}>;
|
|
26
|
+
/** Exposed for testing — resume a paused workflow with agent result */
|
|
27
|
+
export declare function resumeWorkflow(runId: string, agentResult: Record<string, unknown>): Promise<{
|
|
28
|
+
status: 'completed';
|
|
29
|
+
result: unknown;
|
|
30
|
+
} | {
|
|
31
|
+
status: 'waiting_for_agent';
|
|
32
|
+
runId: string;
|
|
33
|
+
agentRequest: object;
|
|
34
|
+
agentChannel: AgentChannel;
|
|
35
|
+
} | {
|
|
36
|
+
status: 'error';
|
|
37
|
+
message: string;
|
|
38
|
+
}>;
|
|
39
|
+
export declare function registerWorkflowRunTools(mcp: McpServer): void;
|
|
40
|
+
//# sourceMappingURL=tools-workflow-run.d.ts.map
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tools for running workflows with waitForAgent pause/resume support.
|
|
3
|
+
*
|
|
4
|
+
* fw_workflow_run: Starts a workflow. If it hits waitForAgent, returns the
|
|
5
|
+
* agent request to the MCP caller (e.g. Claude Code) with status "waiting_for_agent".
|
|
6
|
+
* If the workflow completes without pausing, returns the result directly.
|
|
7
|
+
*
|
|
8
|
+
* fw_workflow_resume: Resumes a paused workflow with the agent's result.
|
|
9
|
+
* Returns the final workflow result (or pauses again if another waitForAgent is hit).
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { executeWorkflowFromFile } from './workflow-executor.js';
|
|
13
|
+
import { AgentChannel } from './agent-channel.js';
|
|
14
|
+
import { storePendingRun, getPendingRun, removePendingRun, } from './run-registry.js';
|
|
15
|
+
function makeToolResult(data) {
|
|
16
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
17
|
+
}
|
|
18
|
+
function makeErrorResult(code, message) {
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: 'text', text: JSON.stringify({ error: code, message }) }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function generateRunId() {
|
|
25
|
+
return `run-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Race between workflow completion and agent pause.
|
|
29
|
+
* Returns whichever happens first.
|
|
30
|
+
*/
|
|
31
|
+
async function raceAgentPause(executionPromise, agentChannel) {
|
|
32
|
+
try {
|
|
33
|
+
const outcome = await Promise.race([
|
|
34
|
+
executionPromise.then((r) => ({
|
|
35
|
+
type: 'completed',
|
|
36
|
+
result: r?.result ?? r,
|
|
37
|
+
})),
|
|
38
|
+
agentChannel.onPause().then((request) => ({
|
|
39
|
+
type: 'waiting_for_agent',
|
|
40
|
+
request,
|
|
41
|
+
})),
|
|
42
|
+
]);
|
|
43
|
+
return outcome;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return {
|
|
47
|
+
type: 'error',
|
|
48
|
+
message: err instanceof Error ? err.message : String(err),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Exposed for testing — run workflow with pause/resume support */
|
|
53
|
+
export async function runWorkflowWithAgent(filePath, params, workflowName) {
|
|
54
|
+
const agentChannel = new AgentChannel();
|
|
55
|
+
const runId = generateRunId();
|
|
56
|
+
const executionPromise = executeWorkflowFromFile(filePath, params, {
|
|
57
|
+
workflowName,
|
|
58
|
+
agentChannel,
|
|
59
|
+
includeTrace: true,
|
|
60
|
+
});
|
|
61
|
+
const outcome = await raceAgentPause(executionPromise, agentChannel);
|
|
62
|
+
if (outcome.type === 'completed') {
|
|
63
|
+
return { status: 'completed', result: outcome.result };
|
|
64
|
+
}
|
|
65
|
+
if (outcome.type === 'waiting_for_agent') {
|
|
66
|
+
storePendingRun({
|
|
67
|
+
runId,
|
|
68
|
+
filePath,
|
|
69
|
+
workflowName,
|
|
70
|
+
executionPromise,
|
|
71
|
+
agentChannel,
|
|
72
|
+
request: outcome.request,
|
|
73
|
+
createdAt: Date.now(),
|
|
74
|
+
tmpFiles: [],
|
|
75
|
+
});
|
|
76
|
+
return { status: 'waiting_for_agent', runId, agentRequest: outcome.request, agentChannel };
|
|
77
|
+
}
|
|
78
|
+
return { status: 'error', message: outcome.message };
|
|
79
|
+
}
|
|
80
|
+
/** Exposed for testing — resume a paused workflow with agent result */
|
|
81
|
+
export async function resumeWorkflow(runId, agentResult) {
|
|
82
|
+
const pendingRun = getPendingRun(runId);
|
|
83
|
+
if (!pendingRun) {
|
|
84
|
+
return { status: 'error', message: `No pending run found with ID "${runId}".` };
|
|
85
|
+
}
|
|
86
|
+
pendingRun.agentChannel.resume(agentResult);
|
|
87
|
+
const outcome = await raceAgentPause(pendingRun.executionPromise, pendingRun.agentChannel);
|
|
88
|
+
if (outcome.type === 'completed') {
|
|
89
|
+
removePendingRun(runId);
|
|
90
|
+
return { status: 'completed', result: outcome.result };
|
|
91
|
+
}
|
|
92
|
+
if (outcome.type === 'waiting_for_agent') {
|
|
93
|
+
pendingRun.request = outcome.request;
|
|
94
|
+
return { status: 'waiting_for_agent', runId, agentRequest: outcome.request, agentChannel: pendingRun.agentChannel };
|
|
95
|
+
}
|
|
96
|
+
removePendingRun(runId);
|
|
97
|
+
return { status: 'error', message: outcome.message };
|
|
98
|
+
}
|
|
99
|
+
export function registerWorkflowRunTools(mcp) {
|
|
100
|
+
// -------------------------------------------------------------------------
|
|
101
|
+
// fw_workflow_run - Start a workflow, pause at waitForAgent
|
|
102
|
+
// -------------------------------------------------------------------------
|
|
103
|
+
mcp.tool('fw_workflow_run', 'Run a workflow. If it hits a waitForAgent node, returns the agent request so ' +
|
|
104
|
+
'you can respond. If it completes without pausing, returns the result directly.', {
|
|
105
|
+
filePath: z.string().describe('Path to the workflow .ts file'),
|
|
106
|
+
params: z
|
|
107
|
+
.record(z.unknown())
|
|
108
|
+
.optional()
|
|
109
|
+
.describe('Input parameters for the workflow'),
|
|
110
|
+
workflowName: z
|
|
111
|
+
.string()
|
|
112
|
+
.optional()
|
|
113
|
+
.describe('Specific workflow export name (for multi-workflow files)'),
|
|
114
|
+
}, async (args) => {
|
|
115
|
+
try {
|
|
116
|
+
const result = await runWorkflowWithAgent(args.filePath, args.params ?? {}, args.workflowName);
|
|
117
|
+
if (result.status === 'error') {
|
|
118
|
+
return makeErrorResult('EXECUTION_ERROR', result.message);
|
|
119
|
+
}
|
|
120
|
+
// Don't leak agentChannel to MCP output
|
|
121
|
+
const { agentChannel: _ac, ...output } = result;
|
|
122
|
+
return makeToolResult(output);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
return makeErrorResult('EXECUTION_ERROR', err instanceof Error ? err.message : String(err));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// -------------------------------------------------------------------------
|
|
129
|
+
// fw_workflow_resume - Resume a paused workflow with agent result
|
|
130
|
+
// -------------------------------------------------------------------------
|
|
131
|
+
mcp.tool('fw_workflow_resume', 'Resume a workflow that paused at waitForAgent. Provide the agent result to continue execution.', {
|
|
132
|
+
runId: z.string().describe('Run ID from fw_workflow_run'),
|
|
133
|
+
agentResult: z
|
|
134
|
+
.record(z.unknown())
|
|
135
|
+
.describe('The agent result to pass back to the workflow'),
|
|
136
|
+
}, async (args) => {
|
|
137
|
+
try {
|
|
138
|
+
const result = await resumeWorkflow(args.runId, args.agentResult);
|
|
139
|
+
if (result.status === 'error') {
|
|
140
|
+
return makeErrorResult('RESUME_ERROR', result.message);
|
|
141
|
+
}
|
|
142
|
+
const { agentChannel: _ac, ...output } = result;
|
|
143
|
+
return makeToolResult(output);
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
return makeErrorResult('RESUME_ERROR', err instanceof Error ? err.message : String(err));
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=tools-workflow-run.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synergenius/flow-weaver",
|
|
3
|
-
"version": "0.33.
|
|
3
|
+
"version": "0.33.1",
|
|
4
4
|
"description": "Flow Weaver: deterministic TypeScript workflow compiler. Define workflows with JSDoc annotations, compile to standalone functions with zero runtime dependencies.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|