hoi4-agent-tools 4.3.0 → 4.5.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 +33 -1
- package/dist/hoi4_agent_tools/core/bounded-source.js +31 -9
- package/dist/hoi4_agent_tools/core/configuration.js +11 -0
- package/dist/hoi4_agent_tools/core/managed-outputs.js +26 -6
- package/dist/hoi4_agent_tools/core/operation-context.js +17 -1
- package/dist/hoi4_agent_tools/core/source/encoding.js +56 -2
- package/dist/hoi4_agent_tools/core/source/parser.js +16 -1
- package/dist/hoi4_agent_tools/core/temporary-directories.js +7 -2
- package/dist/hoi4_agent_tools/core/transaction-limits.js +18 -4
- package/dist/hoi4_agent_tools/core/transactions.js +23 -4
- package/dist/hoi4_agent_tools/core/workspace-json.js +54 -0
- package/dist/hoi4_agent_tools/core/workspace.js +42 -6
- package/dist/hoi4_agent_tools/focus/drift.js +34 -0
- package/dist/hoi4_agent_tools/focus/service.js +9 -3
- package/dist/hoi4_agent_tools/focus/tool-operations.js +2 -0
- package/dist/hoi4_agent_tools/map/appearance-validation.js +1110 -0
- package/dist/hoi4_agent_tools/map/bmp.js +9 -1
- package/dist/hoi4_agent_tools/map/border-quality-input.js +209 -0
- package/dist/hoi4_agent_tools/map/border-quality.js +1180 -0
- package/dist/hoi4_agent_tools/map/colormap.js +128 -0
- package/dist/hoi4_agent_tools/map/geography/geometry.js +284 -0
- package/dist/hoi4_agent_tools/map/geography/index.js +6 -0
- package/dist/hoi4_agent_tools/map/geography/model.js +196 -0
- package/dist/hoi4_agent_tools/map/geography/preparation.js +120 -0
- package/dist/hoi4_agent_tools/map/geography/preview.js +647 -0
- package/dist/hoi4_agent_tools/map/geography/raster.js +253 -0
- package/dist/hoi4_agent_tools/map/geography/resolve.js +1467 -0
- package/dist/hoi4_agent_tools/map/model.js +17 -3
- package/dist/hoi4_agent_tools/map/operations.js +284 -14
- package/dist/hoi4_agent_tools/map/partition-fields.js +398 -0
- package/dist/hoi4_agent_tools/map/partition-geography.js +113 -0
- package/dist/hoi4_agent_tools/map/partition-graph.js +165 -0
- package/dist/hoi4_agent_tools/map/partition-groups.js +164 -0
- package/dist/hoi4_agent_tools/map/partition-provinces.js +727 -0
- package/dist/hoi4_agent_tools/map/partition-tuning.js +101 -0
- package/dist/hoi4_agent_tools/map/partition-watershed.js +252 -0
- package/dist/hoi4_agent_tools/map/partition.js +163 -0
- package/dist/hoi4_agent_tools/map/reference-import.js +333 -0
- package/dist/hoi4_agent_tools/map/reference-preparation.js +142 -6
- package/dist/hoi4_agent_tools/map/reference-sheets.js +231 -0
- package/dist/hoi4_agent_tools/map/reference-world.js +104 -13
- package/dist/hoi4_agent_tools/map/relief/checks.js +281 -0
- package/dist/hoi4_agent_tools/map/relief/fields.js +15 -0
- package/dist/hoi4_agent_tools/map/relief/full-resolution.js +133 -0
- package/dist/hoi4_agent_tools/map/relief/grid.js +190 -0
- package/dist/hoi4_agent_tools/map/relief/hydrology.js +1315 -0
- package/dist/hoi4_agent_tools/map/relief/index.js +27 -0
- package/dist/hoi4_agent_tools/map/relief/land.js +110 -0
- package/dist/hoi4_agent_tools/map/relief/macro-relief.js +535 -0
- package/dist/hoi4_agent_tools/map/relief/math.js +93 -0
- package/dist/hoi4_agent_tools/map/relief/river-raster.js +680 -0
- package/dist/hoi4_agent_tools/map/relief/terrain-synthesis.js +304 -0
- package/dist/hoi4_agent_tools/map/render.js +36 -0
- package/dist/hoi4_agent_tools/map/service.js +206 -16
- package/dist/hoi4_agent_tools/map/tool-operations.js +249 -3
- package/dist/hoi4_agent_tools/map/validation.js +9 -0
- package/dist/hoi4_agent_tools/map/water.js +227 -0
- package/dist/hoi4_agent_tools/map/world-context.js +47 -1
- package/dist/hoi4_agent_tools/map/world-edge.js +335 -0
- package/dist/hoi4_agent_tools/map/world-noise.js +34 -0
- package/dist/hoi4_agent_tools/map/world-outputs.js +22 -10
- package/dist/hoi4_agent_tools/map/world-partition.js +119 -0
- package/dist/hoi4_agent_tools/map/world-provinces.js +131 -56
- package/dist/hoi4_agent_tools/map/world-realms.js +152 -0
- package/dist/hoi4_agent_tools/map/world.js +562 -163
- package/dist/hoi4_agent_tools/mcp/tools/map.js +69 -1
- package/dist/hoi4_agent_tools/reference/operations.js +3 -2
- package/dist/hoi4_agent_tools/schemas/geography.js +325 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +20 -2
- package/dist/hoi4_agent_tools/schemas/map.js +280 -15
- package/dist/hoi4_agent_tools/schemas/reference-sheets.js +92 -0
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/configuration.md +17 -14
- package/docs/focus.md +6 -0
- package/docs/map.md +292 -15
- package/docs/tools.md +4 -4
- package/docs/troubleshooting.md +11 -11
- package/npm-shrinkwrap.json +109 -91
- package/package.json +3 -3
- package/schemas/configuration.schema.json +65 -58
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/decision-inspect-input.schema.json +1 -1
- package/schemas/error-log-input.schema.json +1 -1
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/geography-model.schema.json +1148 -0
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +5 -11
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/helper-expansion-request.schema.json +1 -1
- package/schemas/helper-expansion-summary.schema.json +1 -1
- package/schemas/impact-inspect-input.schema.json +1 -1
- package/schemas/map-operation.schema.json +2325 -292
- package/schemas/mechanic-test-input.schema.json +1 -1
- package/schemas/mod-index-input.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/package-check-input.schema.json +1 -1
- package/schemas/package-manifest.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +25 -7
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/schemas/reference-context-input.schema.json +1 -1
- package/schemas/reference-read-input.schema.json +1 -1
- package/schemas/reference-search-input.schema.json +1 -1
- package/schemas/reference-sheets.schema.json +267 -0
- package/schemas/scenario-suite.schema.json +1 -1
- package/schemas/scenario-test-input.schema.json +1 -1
- package/schemas/script-validate-input.schema.json +1 -1
- package/schemas/shared-scenario.schema.json +1 -1
- package/schemas/source-lookup-input.schema.json +1 -1
- package/server.json +2 -2
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { canonicalJson, canonicalJsonByteLength, sha256Bytes } from '../../core/canonical.js';
|
|
2
|
+
import { ServiceError } from '../../core/result.js';
|
|
3
|
+
import { prepareSource, readBoundedFile, verifySource } from '../../core/scanner.js';
|
|
4
|
+
import { GEOGRAPHY_SOURCE_MAX_BYTES, geographyModelSchema, } from '../../schemas/geography.js';
|
|
5
|
+
import { resolveGeography } from './resolve.js';
|
|
6
|
+
/** Default world size, mirroring `create_world` for worlds without a reference image. */
|
|
7
|
+
export const GEOGRAPHY_DEFAULT_FRAME = { width: 1_024, height: 512 };
|
|
8
|
+
/** Default size of a world built from a reference image. */
|
|
9
|
+
export const GEOGRAPHY_REFERENCE_FRAME = { width: 5_632, height: 2_048 };
|
|
10
|
+
export function operationHasGeography(operation) {
|
|
11
|
+
return operation.geography !== undefined || operation.geographyPath !== undefined;
|
|
12
|
+
}
|
|
13
|
+
function schemaFailure(error) {
|
|
14
|
+
const issues = error.issues.slice(0, 20).map((issue) => ({
|
|
15
|
+
path: issue.path.map(String).join('.'),
|
|
16
|
+
message: issue.message,
|
|
17
|
+
}));
|
|
18
|
+
return new ServiceError('MAP_GEOGRAPHY_SCHEMA_INVALID', `The geography model does not match schema version 1: ${issues
|
|
19
|
+
.slice(0, 3)
|
|
20
|
+
.map((issue) => `${issue.path || '(root)'}: ${issue.message}`)
|
|
21
|
+
.join('; ')}${error.issues.length > 3 ? ` (and ${error.issues.length - 3} more)` : ''}`, { issueCount: error.issues.length, issues });
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read a geography model from the authorized mod workspace. The same canonical-path guard as
|
|
25
|
+
* `reference.imagePath` applies: workspace-relative `.json` only, resolved against the principal's
|
|
26
|
+
* grants, opened once, size-bounded, hashed from the very bytes that are parsed (no second read, so
|
|
27
|
+
* no swap between hash and use), and re-verified against the opened identity afterwards. The
|
|
28
|
+
* returned evidence is recorded as a transaction read dependency, so a changed file blocks the
|
|
29
|
+
* write as stale.
|
|
30
|
+
*/
|
|
31
|
+
export async function readGeographyModelSource(resolver, workspaceId, requestedPath, principal, signal) {
|
|
32
|
+
// Backslashes separate directories on every platform, so the same request resolves the same way everywhere.
|
|
33
|
+
const relativePath = requestedPath.replaceAll('\\', '/');
|
|
34
|
+
if (!/\.json$/iu.test(relativePath) || relativePath.includes(':'))
|
|
35
|
+
throw new ServiceError('MAP_GEOGRAPHY_PATH_INVALID', 'Geography must name a workspace-relative .json file');
|
|
36
|
+
const resolved = await resolver.resolvePath(workspaceId, relativePath, 'read', ['mod'], principal);
|
|
37
|
+
const source = await prepareSource(resolved.root, relativePath, signal);
|
|
38
|
+
try {
|
|
39
|
+
if (!source.metadata.isFile())
|
|
40
|
+
throw new ServiceError('SOURCE_NOT_REGULAR_FILE', 'Geography source must be a regular file');
|
|
41
|
+
if (source.metadata.size < 1 || source.metadata.size > GEOGRAPHY_SOURCE_MAX_BYTES)
|
|
42
|
+
throw new ServiceError('MAP_GEOGRAPHY_SOURCE_TOO_LARGE', `Geography source must hold between 1 byte and ${GEOGRAPHY_SOURCE_MAX_BYTES} bytes`, { limit: GEOGRAPHY_SOURCE_MAX_BYTES });
|
|
43
|
+
const bytes = await readBoundedFile(source.handle, GEOGRAPHY_SOURCE_MAX_BYTES, signal);
|
|
44
|
+
await verifySource(resolved.root, source);
|
|
45
|
+
const sha256 = sha256Bytes(bytes);
|
|
46
|
+
let text = bytes.toString('utf8');
|
|
47
|
+
if (text.charCodeAt(0) === 0xfeff)
|
|
48
|
+
text = text.slice(1);
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = JSON.parse(text);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new ServiceError('MAP_GEOGRAPHY_JSON_INVALID', 'Geography source is not valid JSON');
|
|
55
|
+
}
|
|
56
|
+
const result = geographyModelSchema.safeParse(parsed);
|
|
57
|
+
if (!result.success)
|
|
58
|
+
throw schemaFailure(result.error);
|
|
59
|
+
return {
|
|
60
|
+
model: result.data,
|
|
61
|
+
bytes: bytes.length,
|
|
62
|
+
evidence: {
|
|
63
|
+
absolutePath: source.canonicalPath,
|
|
64
|
+
displayPath: `mod:${relativePath.replaceAll('\\', '/')}`,
|
|
65
|
+
relativePath: relativePath.replaceAll('\\', '/'),
|
|
66
|
+
rootKind: resolved.root.kind,
|
|
67
|
+
loadOrder: resolved.root.loadOrder,
|
|
68
|
+
size: source.metadata.size,
|
|
69
|
+
modifiedMs: source.metadata.mtimeMs,
|
|
70
|
+
sha256,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await source.handle.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Load (inline or from a workspace file), resolve and validate the geography of one `create_world`
|
|
80
|
+
* operation. Model defects are returned as diagnostics, not thrown; only unusable input (bad path,
|
|
81
|
+
* bad JSON, schema mismatch, oversize, both sources given) throws.
|
|
82
|
+
*/
|
|
83
|
+
export async function prepareGeography(resolver, workspaceId, operation, principal, signal) {
|
|
84
|
+
if (operation.geography !== undefined && operation.geographyPath !== undefined)
|
|
85
|
+
throw new ServiceError('MAP_GEOGRAPHY_SOURCE_AMBIGUOUS', 'Give the geography inline (geography) or by file (geographyPath), not both');
|
|
86
|
+
const hasReference = operation.reference !== undefined;
|
|
87
|
+
const frame = {
|
|
88
|
+
width: operation.width ?? (hasReference ? GEOGRAPHY_REFERENCE_FRAME : GEOGRAPHY_DEFAULT_FRAME).width,
|
|
89
|
+
height: operation.height ??
|
|
90
|
+
(hasReference ? GEOGRAPHY_REFERENCE_FRAME : GEOGRAPHY_DEFAULT_FRAME).height,
|
|
91
|
+
};
|
|
92
|
+
let model;
|
|
93
|
+
let source;
|
|
94
|
+
if (operation.geographyPath !== undefined) {
|
|
95
|
+
const loaded = await readGeographyModelSource(resolver, workspaceId, operation.geographyPath, principal, signal);
|
|
96
|
+
model = loaded.model;
|
|
97
|
+
source = {
|
|
98
|
+
kind: 'file',
|
|
99
|
+
bytes: loaded.bytes,
|
|
100
|
+
sha256: loaded.evidence.sha256,
|
|
101
|
+
evidence: loaded.evidence,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
model = operation.geography;
|
|
106
|
+
const bytes = canonicalJsonByteLength(model, GEOGRAPHY_SOURCE_MAX_BYTES);
|
|
107
|
+
if (bytes === undefined)
|
|
108
|
+
throw new ServiceError('MAP_GEOGRAPHY_SOURCE_TOO_LARGE', `The inline geography exceeds ${GEOGRAPHY_SOURCE_MAX_BYTES} bytes; write it to a workspace file and pass geographyPath`, { limit: GEOGRAPHY_SOURCE_MAX_BYTES });
|
|
109
|
+
source = { kind: 'inline', bytes, sha256: sha256Bytes(canonicalJson(model)) };
|
|
110
|
+
}
|
|
111
|
+
signal?.throwIfAborted();
|
|
112
|
+
const resolution = resolveGeography(model, {
|
|
113
|
+
frame,
|
|
114
|
+
hasReference,
|
|
115
|
+
...(operation.edge === undefined ? {} : { edge: operation.edge }),
|
|
116
|
+
operationId: operation.id,
|
|
117
|
+
});
|
|
118
|
+
const diagnostics = resolution.diagnostics;
|
|
119
|
+
return { ...resolution, diagnostics, operationId: operation.id, source, frame, hasReference };
|
|
120
|
+
}
|