hoi4-agent-tools 1.0.0 → 1.1.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/CHANGELOG.md +9 -0
- package/README.md +8 -2
- package/dist/hoi4_agent_tools/focus/compact.js +432 -0
- package/dist/hoi4_agent_tools/focus/compiler.js +1 -7
- package/dist/hoi4_agent_tools/focus/geometry.js +203 -16
- package/dist/hoi4_agent_tools/focus/index.js +1 -0
- package/dist/hoi4_agent_tools/focus/layout.js +1256 -92
- package/dist/hoi4_agent_tools/focus/limits.js +1 -1
- package/dist/hoi4_agent_tools/focus/lint.js +11 -3
- package/dist/hoi4_agent_tools/focus/planning.js +6 -1
- package/dist/hoi4_agent_tools/focus/render.js +14 -9
- package/dist/hoi4_agent_tools/mcp/server/context-schemas.js +8 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +0 -1
- package/dist/hoi4_agent_tools/mcp/server/result.js +123 -52
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +93 -7
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +12 -9
- package/dist/hoi4_agent_tools/mcp/tools/map.js +6 -4
- package/dist/hoi4_agent_tools/mcp/tools/mods.js +1 -1
- package/dist/hoi4_agent_tools/schemas/focus.js +64 -8
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/focus.md +17 -7
- package/docs/setup.md +1 -1
- package/package.json +3 -3
- package/server.json +2 -2
|
@@ -8,6 +8,7 @@ import { GuiHelperDocumentSchema, GuiPreviewScenarioSchema, GUI_TEXT_PACKAGE_MAX
|
|
|
8
8
|
import { workspaceIdSchema, workspaceRelativePathSchema } from '../../schemas/common.js';
|
|
9
9
|
import { PACKAGE_VERSION } from '../../version.js';
|
|
10
10
|
import { requireServerScope } from '../server/base-tools.js';
|
|
11
|
+
import { compactValidatedInputSchema } from '../server/context-schemas.js';
|
|
11
12
|
import { autonomousFailureContext, autonomousResultArtifacts, executePlannedTransaction, } from '../server/transaction-execution.js';
|
|
12
13
|
import { indexSkippedSourceSchema, nonNegativeIntegerSchema, sha256Schema, } from '../server/output-schemas.js';
|
|
13
14
|
import { progressReporter } from '../server/progress.js';
|
|
@@ -88,19 +89,21 @@ const previewStateSchema = z.enum([
|
|
|
88
89
|
'long-text',
|
|
89
90
|
'missing-localisation',
|
|
90
91
|
]);
|
|
92
|
+
const compactGuiScenarioSchema = compactValidatedInputSchema(GuiPreviewScenarioSchema, `Complete GUI preview scenario: https://github.com/klimPaskov/hoi4-agent-tools/blob/v${PACKAGE_VERSION}/docs/gui.md`);
|
|
93
|
+
const compactGuiHelperSchema = compactValidatedInputSchema(GuiHelperDocumentSchema, `Complete GUI helper document: https://github.com/klimPaskov/hoi4-agent-tools/blob/v${PACKAGE_VERSION}/docs/gui.md`);
|
|
91
94
|
const guiBaseInput = z
|
|
92
95
|
.object({
|
|
93
96
|
workspaceId: workspaceIdSchema,
|
|
94
97
|
windowName: z.string().min(1).max(256),
|
|
95
|
-
scenario:
|
|
98
|
+
scenario: compactGuiScenarioSchema,
|
|
96
99
|
})
|
|
97
100
|
.strict();
|
|
98
101
|
const guiInspectInput = z
|
|
99
102
|
.object({
|
|
100
103
|
workspaceId: workspaceIdSchema,
|
|
101
104
|
windowName: z.string().min(1).max(256).optional(),
|
|
102
|
-
scenario:
|
|
103
|
-
relatedScenarios: z.array(
|
|
105
|
+
scenario: compactGuiScenarioSchema.optional(),
|
|
106
|
+
relatedScenarios: z.array(compactGuiScenarioSchema).max(32).optional(),
|
|
104
107
|
})
|
|
105
108
|
.strict()
|
|
106
109
|
.superRefine((value, context) => {
|
|
@@ -142,7 +145,7 @@ export function registerGuiTools(server, engine, context) {
|
|
|
142
145
|
const studio = new ScriptedGuiStudio(engine);
|
|
143
146
|
server.registerTool('hoi4.gui_inspect', {
|
|
144
147
|
title: 'Inspect scripted GUI',
|
|
145
|
-
description: 'Inspect GUI, GFX,
|
|
148
|
+
description: 'Inspect GUI, GFX, script, localisation, and animation sources. Window/scenario selectors add offline visual and interaction diagnostics.',
|
|
146
149
|
inputSchema: guiInspectInput,
|
|
147
150
|
outputSchema: guiScanOutputSchema,
|
|
148
151
|
annotations: artifactProducing,
|
|
@@ -315,7 +318,7 @@ export function registerGuiTools(server, engine, context) {
|
|
|
315
318
|
.extend({
|
|
316
319
|
states: z.array(previewStateSchema).max(14).optional(),
|
|
317
320
|
resolutions: z.array(resolutionSchema).min(1).max(16).optional(),
|
|
318
|
-
comparisonScenario:
|
|
321
|
+
comparisonScenario: compactGuiScenarioSchema.optional(),
|
|
319
322
|
})
|
|
320
323
|
.strict()
|
|
321
324
|
.superRefine(({ scenario, states, resolutions }, context) => {
|
|
@@ -351,23 +354,23 @@ export function registerGuiTools(server, engine, context) {
|
|
|
351
354
|
});
|
|
352
355
|
server.registerTool('hoi4.gui_render', {
|
|
353
356
|
title: 'Render scripted GUI artifacts',
|
|
354
|
-
description: 'Render deterministic
|
|
357
|
+
description: 'Render deterministic offline review artifacts for one GUI window and scenario.',
|
|
355
358
|
inputSchema: renderInput,
|
|
356
359
|
outputSchema: guiRenderOutputSchema,
|
|
357
360
|
annotations: artifactProducing,
|
|
358
361
|
}, (input, extra) => render(input, extra, 'GUI_RENDERED'));
|
|
359
362
|
server.registerTool('hoi4.gui_rewrite', {
|
|
360
363
|
title: 'Create or clean up scripted GUI',
|
|
361
|
-
description: '
|
|
364
|
+
description: 'Apply one validated source, helper, or exact-patch GUI package. Text dependencies use additionalFiles; binary art stays workspace-referenced.',
|
|
362
365
|
inputSchema: z
|
|
363
366
|
.object({
|
|
364
367
|
mode: z.enum(['source', 'helpers', 'patches']),
|
|
365
368
|
workspaceId: workspaceIdSchema,
|
|
366
369
|
relativePath: workspaceRelativePathSchema,
|
|
367
370
|
windowName: z.string().min(1).max(256),
|
|
368
|
-
scenario:
|
|
371
|
+
scenario: compactGuiScenarioSchema,
|
|
369
372
|
source: z.string().max(SOURCE_MAX_BYTES).optional(),
|
|
370
|
-
helper:
|
|
373
|
+
helper: compactGuiHelperSchema.optional(),
|
|
371
374
|
additionalFiles: z
|
|
372
375
|
.array(z
|
|
373
376
|
.object({
|
|
@@ -7,6 +7,7 @@ import { workspaceIdSchema } from '../../schemas/common.js';
|
|
|
7
7
|
import { mapAllocationRequestSchema, mapOperationSchema } from '../../schemas/map.js';
|
|
8
8
|
import { PACKAGE_VERSION } from '../../version.js';
|
|
9
9
|
import { requireServerScope } from '../server/base-tools.js';
|
|
10
|
+
import { compactValidatedInputSchema } from '../server/context-schemas.js';
|
|
10
11
|
import { autonomousFailureContext, autonomousResultArtifacts, executePlannedTransaction, } from '../server/transaction-execution.js';
|
|
11
12
|
import { allocationEvidenceSchema, bitmapRenderHashesSchema, nonNegativeIntegerSchema, operationBlockerDataSchema, sha256Schema, } from '../server/output-schemas.js';
|
|
12
13
|
import { progressReporter } from '../server/progress.js';
|
|
@@ -235,6 +236,7 @@ const mapOverlaySchema = z.enum([
|
|
|
235
236
|
'unit-positions',
|
|
236
237
|
'weather-positions',
|
|
237
238
|
]);
|
|
239
|
+
const compactMapOperationSchema = compactValidatedInputSchema(mapOperationSchema, `Complete map operation: https://github.com/klimPaskov/hoi4-agent-tools/blob/v${PACKAGE_VERSION}/docs/map.md`);
|
|
238
240
|
function validationSummary(validation) {
|
|
239
241
|
return { passed: validation.passed, checks: validation.checks };
|
|
240
242
|
}
|
|
@@ -346,7 +348,7 @@ export function registerMapTools(server, engine, context) {
|
|
|
346
348
|
const nudger = new AgentNudger(engine);
|
|
347
349
|
server.registerTool('hoi4.map_inspect', {
|
|
348
350
|
title: 'Inspect HOI4 map',
|
|
349
|
-
description: '
|
|
351
|
+
description: 'Validate the map and write linked inspection evidence. ID selectors return bounded records; provinceIds also export exact row-run geometry.',
|
|
350
352
|
inputSchema: z
|
|
351
353
|
.object({
|
|
352
354
|
workspaceId: workspaceIdSchema,
|
|
@@ -499,7 +501,7 @@ export function registerMapTools(server, engine, context) {
|
|
|
499
501
|
});
|
|
500
502
|
server.registerTool('hoi4.map_render', {
|
|
501
503
|
title: 'Render map inspection artifacts',
|
|
502
|
-
description: 'Render deterministic
|
|
504
|
+
description: 'Render deterministic offline map artifacts for one layer and bounded overlays.',
|
|
503
505
|
inputSchema: z
|
|
504
506
|
.object({
|
|
505
507
|
workspaceId: workspaceIdSchema,
|
|
@@ -540,11 +542,11 @@ export function registerMapTools(server, engine, context) {
|
|
|
540
542
|
});
|
|
541
543
|
server.registerTool('hoi4.map_rewrite', {
|
|
542
544
|
title: 'Create or clean up map content',
|
|
543
|
-
description: '
|
|
545
|
+
description: 'Apply validated declarative map operations in one call and return linked pixel and semantic evidence.',
|
|
544
546
|
inputSchema: z
|
|
545
547
|
.object({
|
|
546
548
|
workspaceId: workspaceIdSchema,
|
|
547
|
-
operations: z.array(
|
|
549
|
+
operations: z.array(compactMapOperationSchema).min(1).max(100),
|
|
548
550
|
diffScale: z.number().int().min(1).max(16).optional(),
|
|
549
551
|
})
|
|
550
552
|
.strict(),
|
|
@@ -20,7 +20,7 @@ const modsOutputSchema = strictOperationResultSchema(z
|
|
|
20
20
|
export function registerModsTool(server, engine, context) {
|
|
21
21
|
server.registerTool('hoi4.mods', {
|
|
22
22
|
title: 'List available HOI4 mods',
|
|
23
|
-
description: 'List
|
|
23
|
+
description: 'List configured HOI4 mod workspaces, optionally selecting one by ID.',
|
|
24
24
|
inputSchema: z.object({ workspaceId: z.string().min(1).max(64).optional() }).strict(),
|
|
25
25
|
outputSchema: modsOutputSchema,
|
|
26
26
|
annotations: {
|
|
@@ -32,6 +32,14 @@ const prerequisitesSchema = z
|
|
|
32
32
|
.max(10_000),
|
|
33
33
|
})
|
|
34
34
|
.strict();
|
|
35
|
+
const automaticPositionSchema = z
|
|
36
|
+
.object({
|
|
37
|
+
mode: z.literal('auto'),
|
|
38
|
+
pinned: z.literal(false),
|
|
39
|
+
preferredX: z.number().int().min(-100_000).max(100_000).optional(),
|
|
40
|
+
preferredY: z.number().int().min(-100_000).max(100_000).optional(),
|
|
41
|
+
})
|
|
42
|
+
.strict();
|
|
35
43
|
const positionSchema = z.discriminatedUnion('mode', [
|
|
36
44
|
z
|
|
37
45
|
.object({
|
|
@@ -50,14 +58,7 @@ const positionSchema = z.discriminatedUnion('mode', [
|
|
|
50
58
|
pinned: z.boolean(),
|
|
51
59
|
})
|
|
52
60
|
.strict(),
|
|
53
|
-
|
|
54
|
-
.object({
|
|
55
|
-
mode: z.literal('auto'),
|
|
56
|
-
pinned: z.literal(false),
|
|
57
|
-
preferredX: z.number().int().min(-100_000).max(100_000).optional(),
|
|
58
|
-
preferredY: z.number().int().min(-100_000).max(100_000).optional(),
|
|
59
|
-
})
|
|
60
|
-
.strict(),
|
|
61
|
+
automaticPositionSchema,
|
|
61
62
|
]);
|
|
62
63
|
const routeLockSchema = z
|
|
63
64
|
.object({
|
|
@@ -305,6 +306,7 @@ export const focusPlanningSidecarSchema = z
|
|
|
305
306
|
aiStrategyIds: z.array(z.string()),
|
|
306
307
|
payoff: z.string().optional(),
|
|
307
308
|
terminalKind: focusNodeSchema.shape.terminalKind,
|
|
309
|
+
autoPosition: automaticPositionSchema.optional(),
|
|
308
310
|
})
|
|
309
311
|
.strict()),
|
|
310
312
|
})
|
|
@@ -317,12 +319,65 @@ export const focusLayoutDecisionSchema = z
|
|
|
317
319
|
'relative',
|
|
318
320
|
'placed',
|
|
319
321
|
'moved_for_collision',
|
|
322
|
+
'moved_for_spacing',
|
|
320
323
|
'moved_for_mutual_exclusion',
|
|
324
|
+
'moved_for_parent_order',
|
|
325
|
+
'moved_for_lane_bounds',
|
|
321
326
|
'moved_to_reduce_crossings',
|
|
322
327
|
]),
|
|
323
328
|
message: z.string(),
|
|
324
329
|
})
|
|
325
330
|
.strict();
|
|
331
|
+
export const focusLayoutMetricsSchema = z
|
|
332
|
+
.object({
|
|
333
|
+
bounds: z
|
|
334
|
+
.object({
|
|
335
|
+
minimumX: z.number().int(),
|
|
336
|
+
maximumX: z.number().int(),
|
|
337
|
+
minimumY: z.number().int(),
|
|
338
|
+
maximumY: z.number().int(),
|
|
339
|
+
columnSpan: z.number().int().nonnegative(),
|
|
340
|
+
rowSpan: z.number().int().nonnegative(),
|
|
341
|
+
columnCount: z.number().int().nonnegative(),
|
|
342
|
+
rowCount: z.number().int().nonnegative(),
|
|
343
|
+
})
|
|
344
|
+
.strict(),
|
|
345
|
+
connectors: z
|
|
346
|
+
.object({
|
|
347
|
+
count: z.number().int().nonnegative(),
|
|
348
|
+
crossingCount: z.number().int().nonnegative(),
|
|
349
|
+
nodeIntersectionCount: z.number().int().nonnegative(),
|
|
350
|
+
longConnectorCount: z.number().int().nonnegative(),
|
|
351
|
+
totalHorizontalSpan: z.number().int().nonnegative(),
|
|
352
|
+
maximumHorizontalSpan: z.number().int().nonnegative(),
|
|
353
|
+
totalVerticalSpan: z.number().int().nonnegative(),
|
|
354
|
+
maximumVerticalSpan: z.number().int().nonnegative(),
|
|
355
|
+
totalManhattanSpan: z.number().int().nonnegative(),
|
|
356
|
+
maximumManhattanSpan: z.number().int().nonnegative(),
|
|
357
|
+
})
|
|
358
|
+
.strict(),
|
|
359
|
+
symmetry: z
|
|
360
|
+
.object({
|
|
361
|
+
siblingCohortCount: z.number().int().nonnegative(),
|
|
362
|
+
asymmetricSiblingCohortCount: z.number().int().nonnegative(),
|
|
363
|
+
totalSiblingDeviation: z.number().int().nonnegative(),
|
|
364
|
+
maximumSiblingDeviation: z.number().int().nonnegative(),
|
|
365
|
+
offAnchorSiblingCohortCount: z.number().int().nonnegative(),
|
|
366
|
+
totalSiblingAnchorDeviation: z.number().int().nonnegative(),
|
|
367
|
+
maximumSiblingAnchorDeviation: z.number().int().nonnegative(),
|
|
368
|
+
boundingCenterOffsetTwice: z.number().int().nonnegative(),
|
|
369
|
+
})
|
|
370
|
+
.strict(),
|
|
371
|
+
spacing: z
|
|
372
|
+
.object({
|
|
373
|
+
requiredSameRowSpacing: z.number().int().positive(),
|
|
374
|
+
sameRowPairCount: z.number().int().nonnegative(),
|
|
375
|
+
tooCloseSameRowPairCount: z.number().int().nonnegative(),
|
|
376
|
+
minimumSameRowSpacing: z.number().int().nonnegative(),
|
|
377
|
+
})
|
|
378
|
+
.strict(),
|
|
379
|
+
})
|
|
380
|
+
.strict();
|
|
326
381
|
export const focusLayoutSchema = z
|
|
327
382
|
.object({
|
|
328
383
|
treeId: z.string(),
|
|
@@ -338,6 +393,7 @@ export const focusLayoutSchema = z
|
|
|
338
393
|
.strict()),
|
|
339
394
|
decisions: z.array(focusLayoutDecisionSchema),
|
|
340
395
|
diagnostics: z.array(z.unknown()),
|
|
396
|
+
metrics: focusLayoutMetricsSchema.optional(),
|
|
341
397
|
layoutHash: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
342
398
|
})
|
|
343
399
|
.strict();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export const PACKAGE_NAME = 'hoi4-agent-tools';
|
|
3
|
-
export const PACKAGE_VERSION = '1.
|
|
3
|
+
export const PACKAGE_VERSION = '1.1.0';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
6
|
export const TRANSACTION_VERSION = 1;
|
package/docs/focus.md
CHANGED
|
@@ -18,12 +18,22 @@ For a missing target, call `hoi4.focus_rewrite` with `createIfMissing: true`. Re
|
|
|
18
18
|
|
|
19
19
|
## Clean an existing tree
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
For a layout-only cleanup of an existing national tree, call `hoi4.focus_rewrite` without a plan:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"workspaceId": "my-mod",
|
|
26
|
+
"relativePath": "common/national_focus/my_tree.txt",
|
|
27
|
+
"treeId": "my_tree",
|
|
28
|
+
"layoutMode": "compact"
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Compact mode imports the tree, resets authored presentation coordinates and lane bounds, and performs a deterministic reflow in the same rewrite call. It keeps prerequisites, mutual exclusions, availability, bypasses, rewards, icons, localisation, AI behavior, and route/lane membership unchanged.
|
|
33
|
+
|
|
34
|
+
The rewrite measures canvas bounds, same-row spacing, the same curved connector paths used by the renderer, connector lengths, connector-node intersections, branch balance, and overall centering. A bounded refinement pass moves automatic gateways and blockers only when the measured connector result improves. The rewrite refuses changes with close focuses, rendered crossings, excessive branch imbalance, an off-center canvas, or connector and size metrics outside absolute and source-relative limits.
|
|
35
|
+
|
|
36
|
+
Use the default `layoutMode: "authored"` with a complete plan when deliberately designing coordinates or changing gameplay. Set `layoutMode: "compact"` with a complete plan when creating a large automatically arranged tree. Inspect and render calls remain useful before or after either rewrite mode, but a separate plan is not required for compact cleanup.
|
|
27
37
|
|
|
28
38
|
A cleanup should not change gameplay relationships to improve the picture. One `prerequisite` block containing several focus IDs is an OR group; several prerequisite blocks are AND requirements.
|
|
29
39
|
|
|
@@ -35,6 +45,6 @@ If malformed or unsupported script makes a requested change ambiguous, the rewri
|
|
|
35
45
|
|
|
36
46
|
## What inspection checks
|
|
37
47
|
|
|
38
|
-
Inspection and rendering can report missing references, invalid prerequisite structure, duplicate or overlapping positions, route conflicts, missing localisation or sprites, weak terminal branches, repeated rewards, and missing AI metadata. Renders are offline review artifacts, not game screenshots.
|
|
48
|
+
Inspection and rendering can report missing references, invalid prerequisite structure, duplicate or overlapping positions, insufficient spacing, long or crossing connectors, branch asymmetry, route conflicts, missing localisation or sprites, weak terminal branches, repeated rewards, and missing AI metadata. Renders are offline review artifacts, not game screenshots.
|
|
39
49
|
|
|
40
50
|
National trees with 200 or more focuses render review PNGs at half scale by default so large agent workflows stay responsive. Pass an explicit `reviewScale` from `0.25` through `1` when a different raster size is needed; HTML, SVG, JSON, layout, and diagnostics remain complete.
|
package/docs/setup.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hoi4-agent-tools",
|
|
3
3
|
"mcpName": "io.github.klimPaskov/hoi4-agent-tools",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "MCP tools for coding agents to create and clean HOI4 focus trees, scripted GUIs, and maps",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"fast-glob": "3.3.3",
|
|
85
85
|
"fontkit": "2.0.4",
|
|
86
86
|
"iconv-lite": "0.7.3",
|
|
87
|
-
"jose": "6.
|
|
87
|
+
"jose": "6.2.3",
|
|
88
88
|
"sharp": "0.35.3",
|
|
89
89
|
"zod": "4.4.3"
|
|
90
90
|
},
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"@vitest/coverage-v8": "4.1.10",
|
|
99
99
|
"ajv": "8.20.0",
|
|
100
100
|
"ajv-formats": "3.0.1",
|
|
101
|
-
"eslint": "10.
|
|
101
|
+
"eslint": "10.7.0",
|
|
102
102
|
"fast-check": "4.9.0",
|
|
103
103
|
"pixelmatch": "7.2.0",
|
|
104
104
|
"prettier": "3.9.5",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.klimPaskov/hoi4-agent-tools",
|
|
4
4
|
"title": "HOI4 Agent Tools",
|
|
5
5
|
"description": "Create and clean HOI4 focus trees, scripted GUIs, and maps with coding agents.",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.1.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/klimPaskov/hoi4-agent-tools",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "hoi4-agent-tools",
|
|
16
|
-
"version": "1.
|
|
16
|
+
"version": "1.1.0",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|