modwright 0.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/LICENSE +35 -0
- package/README.md +212 -0
- package/assets/claude/agents/mw-explore.md +46 -0
- package/assets/claude/agents/mw-fixture.md +33 -0
- package/assets/claude/agents/mw-rule-author.md +51 -0
- package/assets/claude/agents/mw-scaffold-author.md +48 -0
- package/assets/claude/skills/mw-author.md +59 -0
- package/assets/claude/skills/mw-new-item.md +51 -0
- package/assets/claude/skills/mw-new-spell.md +50 -0
- package/assets/claude/skills/mw-scaffold.md +59 -0
- package/assets/claude/skills/mw-ship.md +57 -0
- package/assets/claude/skills/mw-validate.md +51 -0
- package/assets/claude/skills/mw-verify.md +59 -0
- package/assets/nivalisnights/extract_resources.py +177 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Config.json +5 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapClient.lua +55 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/BootstrapServer.lua +81 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Log.lua +96 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Probes.lua +1038 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/ProbesClient.lua +74 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Protocol.lua +985 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/ScriptExtender/Lua/ModWrightBridge/Watch.lua +175 -0
- package/bridges/bg3-se/Mods/ModWrightBridge/meta.lsx +45 -0
- package/bridges/bg3-se/README.md +99 -0
- package/bridges/bg3-se/bridge.json +68 -0
- package/bridges/cp2077-cet/ModWrightBridge/init.lua +375 -0
- package/bridges/cp2077-cet/ModWrightBridge/probes.lua +1104 -0
- package/bridges/cp2077-cet/ModWrightBridge/protocol.lua +1306 -0
- package/bridges/cp2077-cet/README.md +165 -0
- package/bridges/cp2077-cet/bridge.json +53 -0
- package/dist/core/bridge/arm.js +175 -0
- package/dist/core/bridge/cet-console.js +26 -0
- package/dist/core/bridge/cet-log.js +362 -0
- package/dist/core/bridge/index.js +11 -0
- package/dist/core/bridge/locate.js +35 -0
- package/dist/core/bridge/paths.js +34 -0
- package/dist/core/bridge/pending.js +89 -0
- package/dist/core/bridge/poll.js +155 -0
- package/dist/core/bridge/stage.js +100 -0
- package/dist/core/bridge/status.js +97 -0
- package/dist/core/bridge/types.js +145 -0
- package/dist/core/bridge/validate.js +107 -0
- package/dist/core/build/index.js +6 -0
- package/dist/core/build/manifest.js +47 -0
- package/dist/core/build/paths.js +32 -0
- package/dist/core/build/run.js +342 -0
- package/dist/core/build/stage.js +265 -0
- package/dist/core/build/steps/bg3.js +129 -0
- package/dist/core/build/steps/core.js +195 -0
- package/dist/core/build/steps/cyberpunk.js +95 -0
- package/dist/core/build/steps/listing.js +35 -0
- package/dist/core/build/types.js +13 -0
- package/dist/core/build/zip.js +148 -0
- package/dist/core/compat/check.js +113 -0
- package/dist/core/compat/pe.js +220 -0
- package/dist/core/compat/types.js +1 -0
- package/dist/core/compat/version.js +70 -0
- package/dist/core/deploy/guards.js +292 -0
- package/dist/core/deploy/index.js +5 -0
- package/dist/core/deploy/plan.js +696 -0
- package/dist/core/deploy/reload-apply.js +361 -0
- package/dist/core/deploy/reload.js +358 -0
- package/dist/core/deploy/sources.js +169 -0
- package/dist/core/deploy/types.js +1 -0
- package/dist/core/epic.js +54 -0
- package/dist/core/fsutil.js +191 -0
- package/dist/core/home.js +70 -0
- package/dist/core/knowledge/facts.js +229 -0
- package/dist/core/knowledge/index.js +4 -0
- package/dist/core/knowledge/search.js +112 -0
- package/dist/core/knowledge/store.js +123 -0
- package/dist/core/knowledge/types.js +1 -0
- package/dist/core/ledger/check.js +42 -0
- package/dist/core/ledger/hooks.js +80 -0
- package/dist/core/ledger/index.js +7 -0
- package/dist/core/ledger/query.js +17 -0
- package/dist/core/ledger/record.js +84 -0
- package/dist/core/ledger/status.js +53 -0
- package/dist/core/ledger/store.js +98 -0
- package/dist/core/ledger/types.js +108 -0
- package/dist/core/logs/index.js +4 -0
- package/dist/core/logs/registry.js +50 -0
- package/dist/core/logs/scan.js +195 -0
- package/dist/core/logs/triage.js +160 -0
- package/dist/core/logs/types.js +1 -0
- package/dist/core/lsx.js +10 -0
- package/dist/core/lz4.js +66 -0
- package/dist/core/mods.js +229 -0
- package/dist/core/process.js +49 -0
- package/dist/core/project/build-steps.js +83 -0
- package/dist/core/project/describe.js +74 -0
- package/dist/core/project/index.js +5 -0
- package/dist/core/project/load.js +146 -0
- package/dist/core/project/schema.js +264 -0
- package/dist/core/project/types.js +3 -0
- package/dist/core/prompts.js +72 -0
- package/dist/core/registry.js +35 -0
- package/dist/core/safety/backup.js +206 -0
- package/dist/core/safety/execute.js +188 -0
- package/dist/core/safety/index.js +5 -0
- package/dist/core/safety/plan.js +122 -0
- package/dist/core/safety/restore.js +172 -0
- package/dist/core/safety/types.js +1 -0
- package/dist/core/scaffold/bootstrap.js +7 -0
- package/dist/core/scaffold/catalog.js +28 -0
- package/dist/core/scaffold/claude-assets.js +271 -0
- package/dist/core/scaffold/collisions.js +64 -0
- package/dist/core/scaffold/emit.js +356 -0
- package/dist/core/scaffold/engine.js +680 -0
- package/dist/core/scaffold/ids.js +78 -0
- package/dist/core/scaffold/index.js +10 -0
- package/dist/core/scaffold/jsonpath.js +121 -0
- package/dist/core/scaffold/load.js +81 -0
- package/dist/core/scaffold/render.js +85 -0
- package/dist/core/scaffold/sequence.js +396 -0
- package/dist/core/scaffold/types.js +309 -0
- package/dist/core/steam.js +63 -0
- package/dist/core/surfaceutil.js +36 -0
- package/dist/core/testplan/check.js +67 -0
- package/dist/core/testplan/index.js +5 -0
- package/dist/core/testplan/load.js +100 -0
- package/dist/core/testplan/registry.js +317 -0
- package/dist/core/testplan/run.js +389 -0
- package/dist/core/testplan/types.js +82 -0
- package/dist/core/text/converters/bg3.js +50 -0
- package/dist/core/text/converters/cyberpunk.js +36 -0
- package/dist/core/text/converters/index.js +261 -0
- package/dist/core/text/diff/cr2w.js +180 -0
- package/dist/core/text/diff/index.js +16 -0
- package/dist/core/text/diff/loca.js +43 -0
- package/dist/core/text/diff/lsx.js +126 -0
- package/dist/core/text/handles.js +86 -0
- package/dist/core/text/index.js +6 -0
- package/dist/core/text/json.js +46 -0
- package/dist/core/text/templates/cache.js +71 -0
- package/dist/core/text/templates/index.js +3 -0
- package/dist/core/text/templates/library.js +245 -0
- package/dist/core/text/templates/types.js +51 -0
- package/dist/core/text/toolchain.js +16 -0
- package/dist/core/text/types.js +1 -0
- package/dist/core/text/validate.js +149 -0
- package/dist/core/text/xml.js +58 -0
- package/dist/core/toolchain/datafetch.js +151 -0
- package/dist/core/toolchain/env.js +63 -0
- package/dist/core/toolchain/features.js +170 -0
- package/dist/core/toolchain/index.js +8 -0
- package/dist/core/toolchain/install.js +191 -0
- package/dist/core/toolchain/locate.js +275 -0
- package/dist/core/toolchain/remember.js +84 -0
- package/dist/core/toolchain/run.js +318 -0
- package/dist/core/toolchain/specs.js +351 -0
- package/dist/core/toolchain/types.js +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/userconfig.js +66 -0
- package/dist/core/validate/context.js +139 -0
- package/dist/core/validate/index.js +5 -0
- package/dist/core/validate/registry.js +58 -0
- package/dist/core/validate/run.js +131 -0
- package/dist/core/validate/suppressions.js +91 -0
- package/dist/core/validate/types.js +2 -0
- package/dist/core/yamledit.js +51 -0
- package/dist/index.js +389 -0
- package/dist/server.js +1914 -0
- package/dist/surfaces/baldursgate3/compat.js +164 -0
- package/dist/surfaces/baldursgate3/identity.js +110 -0
- package/dist/surfaces/baldursgate3/index/build.js +331 -0
- package/dist/surfaces/baldursgate3/index/index.js +17 -0
- package/dist/surfaces/baldursgate3/index/info.js +40 -0
- package/dist/surfaces/baldursgate3/index/parsers/loca.js +41 -0
- package/dist/surfaces/baldursgate3/index/parsers/lsx.js +137 -0
- package/dist/surfaces/baldursgate3/index/parsers/stats.js +45 -0
- package/dist/surfaces/baldursgate3/index/parsers/treasuretable.js +57 -0
- package/dist/surfaces/baldursgate3/index/parsers/xmlutil.js +142 -0
- package/dist/surfaces/baldursgate3/index/queries.js +327 -0
- package/dist/surfaces/baldursgate3/index/schema.js +62 -0
- package/dist/surfaces/baldursgate3/index.js +3 -0
- package/dist/surfaces/baldursgate3/logs.js +278 -0
- package/dist/surfaces/baldursgate3/modsettings.js +115 -0
- package/dist/surfaces/baldursgate3/pak.js +153 -0
- package/dist/surfaces/baldursgate3/settings.js +49 -0
- package/dist/surfaces/baldursgate3/surface.js +232 -0
- package/dist/surfaces/baldursgate3/validators/bridge.js +7 -0
- package/dist/surfaces/baldursgate3/validators/files.js +476 -0
- package/dist/surfaces/baldursgate3/validators/items.js +1001 -0
- package/dist/surfaces/baldursgate3/validators/progression.js +734 -0
- package/dist/surfaces/baldursgate3/validators/stats.js +1076 -0
- package/dist/surfaces/baldursgate3/validators/story.js +790 -0
- package/dist/surfaces/baldursgate3/validators/templates.js +7 -0
- package/dist/surfaces/cyberpunk2077/compat.js +223 -0
- package/dist/surfaces/cyberpunk2077/index/build.js +231 -0
- package/dist/surfaces/cyberpunk2077/index/index.js +17 -0
- package/dist/surfaces/cyberpunk2077/index/info.js +51 -0
- package/dist/surfaces/cyberpunk2077/index/model.js +52 -0
- package/dist/surfaces/cyberpunk2077/index/parsers/tweak.js +483 -0
- package/dist/surfaces/cyberpunk2077/index/parsers/tweakxl-yaml.js +183 -0
- package/dist/surfaces/cyberpunk2077/index/queries.js +504 -0
- package/dist/surfaces/cyberpunk2077/index/resource.js +135 -0
- package/dist/surfaces/cyberpunk2077/index/schema.js +90 -0
- package/dist/surfaces/cyberpunk2077/index.js +3 -0
- package/dist/surfaces/cyberpunk2077/logs.js +742 -0
- package/dist/surfaces/cyberpunk2077/paths.js +12 -0
- package/dist/surfaces/cyberpunk2077/surface.js +218 -0
- package/dist/surfaces/cyberpunk2077/validators/bridge.js +7 -0
- package/dist/surfaces/cyberpunk2077/validators/cr2w.js +349 -0
- package/dist/surfaces/cyberpunk2077/validators/garments.js +1181 -0
- package/dist/surfaces/cyberpunk2077/validators/mesh.js +338 -0
- package/dist/surfaces/cyberpunk2077/validators/modsettings.js +120 -0
- package/dist/surfaces/cyberpunk2077/validators/packaging.js +463 -0
- package/dist/surfaces/cyberpunk2077/validators/red4ext.js +318 -0
- package/dist/surfaces/cyberpunk2077/validators/redscript.js +193 -0
- package/dist/surfaces/cyberpunk2077/validators/templates.js +7 -0
- package/dist/surfaces/cyberpunk2077/validators/tweaks.js +768 -0
- package/dist/surfaces/cyberpunk2077/validators/vehicles.js +531 -0
- package/dist/surfaces/cyberpunk2077/validators/xl.js +308 -0
- package/dist/surfaces/eldenring/binders.js +111 -0
- package/dist/surfaces/eldenring/compat.js +166 -0
- package/dist/surfaces/eldenring/dcx.js +49 -0
- package/dist/surfaces/eldenring/index.js +13 -0
- package/dist/surfaces/eldenring/loaders.js +132 -0
- package/dist/surfaces/eldenring/logs.js +67 -0
- package/dist/surfaces/eldenring/paramdef.js +256 -0
- package/dist/surfaces/eldenring/params.js +129 -0
- package/dist/surfaces/eldenring/surface.js +121 -0
- package/dist/surfaces/eldenring/tools.js +52 -0
- package/dist/surfaces/eldenring/validators/binders.js +193 -0
- package/dist/surfaces/eldenring/validators/packages.js +159 -0
- package/dist/surfaces/eldenring/validators/params.js +334 -0
- package/dist/surfaces/eldenring/validators/profiles.js +287 -0
- package/dist/surfaces/index.js +34 -0
- package/dist/surfaces/nivalisnights/compat.js +229 -0
- package/dist/surfaces/nivalisnights/index/build.js +310 -0
- package/dist/surfaces/nivalisnights/index/index.js +5 -0
- package/dist/surfaces/nivalisnights/index/queries.js +231 -0
- package/dist/surfaces/nivalisnights/index.js +4 -0
- package/dist/surfaces/nivalisnights/logs.js +473 -0
- package/dist/surfaces/nivalisnights/surface.js +195 -0
- package/dist/surfaces/skyrimse/archives.js +229 -0
- package/dist/surfaces/skyrimse/compat.js +163 -0
- package/dist/surfaces/skyrimse/index.js +12 -0
- package/dist/surfaces/skyrimse/loadorder.js +174 -0
- package/dist/surfaces/skyrimse/logs.js +241 -0
- package/dist/surfaces/skyrimse/plugins.js +149 -0
- package/dist/surfaces/skyrimse/records.js +94 -0
- package/dist/surfaces/skyrimse/surface.js +240 -0
- package/dist/surfaces/skyrimse/validators/archive-records.js +165 -0
- package/dist/surfaces/skyrimse/validators/archives.js +110 -0
- package/dist/surfaces/skyrimse/validators/plugins.js +374 -0
- package/dist/surfaces/skyrimse/validators/records.js +117 -0
- package/dist/surfaces/stardewvalley/compat.js +190 -0
- package/dist/surfaces/stardewvalley/config.js +42 -0
- package/dist/surfaces/stardewvalley/contentpatcher.js +203 -0
- package/dist/surfaces/stardewvalley/index/index.js +229 -0
- package/dist/surfaces/stardewvalley/index/wiki.js +190 -0
- package/dist/surfaces/stardewvalley/index.js +12 -0
- package/dist/surfaces/stardewvalley/logs.js +307 -0
- package/dist/surfaces/stardewvalley/mods.js +182 -0
- package/dist/surfaces/stardewvalley/surface.js +244 -0
- package/dist/surfaces/stardewvalley/validators/content.js +321 -0
- package/dist/surfaces/stardewvalley/validators/fields.js +147 -0
- package/dist/surfaces/stardewvalley/validators/loadorder.js +69 -0
- package/dist/surfaces/stardewvalley/validators/manifest.js +221 -0
- package/dist/surfaces/subnautica2/compat.js +138 -0
- package/dist/surfaces/subnautica2/index.js +5 -0
- package/dist/surfaces/subnautica2/logs.js +129 -0
- package/dist/surfaces/subnautica2/paks.js +173 -0
- package/dist/surfaces/subnautica2/surface.js +151 -0
- package/dist/surfaces/subnautica2/validators/packaging.js +298 -0
- package/dist/surfaces/valheim/compat.js +171 -0
- package/dist/surfaces/valheim/index/index.js +301 -0
- package/dist/surfaces/valheim/index/tables.js +103 -0
- package/dist/surfaces/valheim/index.js +11 -0
- package/dist/surfaces/valheim/logs.js +197 -0
- package/dist/surfaces/valheim/packaging.js +131 -0
- package/dist/surfaces/valheim/plugins.js +270 -0
- package/dist/surfaces/valheim/references.js +121 -0
- package/dist/surfaces/valheim/surface.js +223 -0
- package/dist/surfaces/valheim/validators/packaging.js +218 -0
- package/dist/surfaces/valheim/validators/plugins.js +287 -0
- package/dist/surfaces/valheim/validators/references.js +169 -0
- package/knowledge/baldursgate3/animations.gr2.yaml +166 -0
- package/knowledge/baldursgate3/audio.voice.yaml +182 -0
- package/knowledge/baldursgate3/audio.wwise.yaml +104 -0
- package/knowledge/baldursgate3/charactercreation.heads-hair.yaml +193 -0
- package/knowledge/baldursgate3/communitylibrary.shared-content.yaml +129 -0
- package/knowledge/baldursgate3/compatibilityframework.api.yaml +176 -0
- package/knowledge/baldursgate3/crash.causes.yaml +90 -0
- package/knowledge/baldursgate3/dialogue.dialogbank.yaml +120 -0
- package/knowledge/baldursgate3/dialogue.files.yaml +236 -0
- package/knowledge/baldursgate3/dialogue.osiris-goals.yaml +386 -0
- package/knowledge/baldursgate3/dialogue.yaml +122 -0
- package/knowledge/baldursgate3/feats.classes.yaml +164 -0
- package/knowledge/baldursgate3/items.cloth-physics.yaml +167 -0
- package/knowledge/baldursgate3/items.roottemplates.yaml +320 -0
- package/knowledge/baldursgate3/items.visualbank.yaml +628 -0
- package/knowledge/baldursgate3/journal.quests.yaml +214 -0
- package/knowledge/baldursgate3/loadorder.modsettings.yaml +128 -0
- package/knowledge/baldursgate3/localization.handles.yaml +92 -0
- package/knowledge/baldursgate3/misc.encoding-and-practice.yaml +111 -0
- package/knowledge/baldursgate3/origins.companions.yaml +144 -0
- package/knowledge/baldursgate3/osiris.signatures.yaml +211 -0
- package/knowledge/baldursgate3/packaging.meta.yaml +155 -0
- package/knowledge/baldursgate3/packaging.paks.yaml +79 -0
- package/knowledge/baldursgate3/process.launch.yaml +34 -0
- package/knowledge/baldursgate3/progressions.yaml +92 -0
- package/knowledge/baldursgate3/publishing.modio-nexus.yaml +170 -0
- package/knowledge/baldursgate3/publishing.toolkit-modio.yaml +135 -0
- package/knowledge/baldursgate3/races.progression.yaml +228 -0
- package/knowledge/baldursgate3/scriptextender.console.yaml +257 -0
- package/knowledge/baldursgate3/scriptextender.yaml +252 -0
- package/knowledge/baldursgate3/stats.boosts.yaml +293 -0
- package/knowledge/baldursgate3/stats.equipment-kits.yaml +69 -0
- package/knowledge/baldursgate3/stats.interrupts.yaml +45 -0
- package/knowledge/baldursgate3/stats.itemcombos.yaml +65 -0
- package/knowledge/baldursgate3/stats.spells.yaml +293 -0
- package/knowledge/baldursgate3/stats.weapons.yaml +210 -0
- package/knowledge/baldursgate3/toolchain.divine.yaml +179 -0
- package/knowledge/baldursgate3/toolchain.lslib-formats.yaml +242 -0
- package/knowledge/baldursgate3/toolkit.levels.yaml +177 -0
- package/knowledge/baldursgate3/ui.icons-atlas.yaml +78 -0
- package/knowledge/baldursgate3/ui.icons.yaml +412 -0
- package/knowledge/baldursgate3/ui.mcm.yaml +161 -0
- package/knowledge/baldursgate3/vfx.materials.yaml +174 -0
- package/knowledge/baldursgate3/visuals.characterfix.yaml +73 -0
- package/knowledge/cyberpunk2077/animations.anims.yaml +118 -0
- package/knowledge/cyberpunk2077/anims.archivexl.yaml +163 -0
- package/knowledge/cyberpunk2077/anims.authoring.yaml +579 -0
- package/knowledge/cyberpunk2077/anims.encoder.yaml +156 -0
- package/knowledge/cyberpunk2077/anims.graph.yaml +629 -0
- package/knowledge/cyberpunk2077/anims.method.yaml +360 -0
- package/knowledge/cyberpunk2077/anims.pipeline.yaml +352 -0
- package/knowledge/cyberpunk2077/anims.roundtrip.yaml +222 -0
- package/knowledge/cyberpunk2077/anims.sets.yaml +176 -0
- package/knowledge/cyberpunk2077/archivexl.bodytypes.yaml +149 -0
- package/knowledge/cyberpunk2077/archivexl.dynamic-appearances.yaml +218 -0
- package/knowledge/cyberpunk2077/archivexl.manifest.yaml +214 -0
- package/knowledge/cyberpunk2077/audio.events.yaml +168 -0
- package/knowledge/cyberpunk2077/audio.sounds.yaml +164 -0
- package/knowledge/cyberpunk2077/audio.voicesets.yaml +146 -0
- package/knowledge/cyberpunk2077/cet.sandbox.yaml +219 -0
- package/knowledge/cyberpunk2077/clothing.garments.yaml +283 -0
- package/knowledge/cyberpunk2077/clothing.refits.yaml +215 -0
- package/knowledge/cyberpunk2077/codeware.overview.yaml +111 -0
- package/knowledge/cyberpunk2077/codeware.systems.yaml +192 -0
- package/knowledge/cyberpunk2077/drones.combat.yaml +416 -0
- package/knowledge/cyberpunk2077/drones.control.yaml +552 -0
- package/knowledge/cyberpunk2077/entities.appearance.yaml +166 -0
- package/knowledge/cyberpunk2077/equipmentex.slots.yaml +151 -0
- package/knowledge/cyberpunk2077/fx.effects.yaml +246 -0
- package/knowledge/cyberpunk2077/fx.lights.yaml +171 -0
- package/knowledge/cyberpunk2077/hair.modding.yaml +145 -0
- package/knowledge/cyberpunk2077/items.wiring.yaml +246 -0
- package/knowledge/cyberpunk2077/loadorder.archives.yaml +71 -0
- package/knowledge/cyberpunk2077/mesh.blender.yaml +209 -0
- package/knowledge/cyberpunk2077/nativedb.dump.yaml +98 -0
- package/knowledge/cyberpunk2077/npc.appearance.yaml +275 -0
- package/knowledge/cyberpunk2077/npc.behaviour.yaml +128 -0
- package/knowledge/cyberpunk2077/npc.locomotion.yaml +490 -0
- package/knowledge/cyberpunk2077/packaging.deploy.yaml +175 -0
- package/knowledge/cyberpunk2077/packaging.variants.yaml +176 -0
- package/knowledge/cyberpunk2077/player.control.yaml +918 -0
- package/knowledge/cyberpunk2077/process.assetbuild.yaml +154 -0
- package/knowledge/cyberpunk2077/process.lessons.yaml +276 -0
- package/knowledge/cyberpunk2077/quests.minor.yaml +46 -0
- package/knowledge/cyberpunk2077/quests.scenes.yaml +282 -0
- package/knowledge/cyberpunk2077/quickhacks.tiers.yaml +41 -0
- package/knowledge/cyberpunk2077/red4ext.api.yaml +645 -0
- package/knowledge/cyberpunk2077/red4ext.plugins.yaml +124 -0
- package/knowledge/cyberpunk2077/redhottools.hotreload.yaml +245 -0
- package/knowledge/cyberpunk2077/redscript.compile-log.yaml +76 -0
- package/knowledge/cyberpunk2077/redscript.modules.yaml +118 -0
- package/knowledge/cyberpunk2077/toolchain.wolvenkit.yaml +163 -0
- package/knowledge/cyberpunk2077/tweakdb.loot-recipes.yaml +184 -0
- package/knowledge/cyberpunk2077/tweakdb.melee.yaml +352 -0
- package/knowledge/cyberpunk2077/tweakdb.weapons.yaml +325 -0
- package/knowledge/cyberpunk2077/tweakxl.hotreload.yaml +151 -0
- package/knowledge/cyberpunk2077/tweakxl.yaml-syntax.yaml +208 -0
- package/knowledge/cyberpunk2077/ui.icons.yaml +259 -0
- package/knowledge/cyberpunk2077/ui.inkatlas.yaml +81 -0
- package/knowledge/cyberpunk2077/ui.modsettings.yaml +204 -0
- package/knowledge/cyberpunk2077/vehicles.control.yaml +475 -0
- package/knowledge/cyberpunk2077/vehicles.dashboard-ui.yaml +203 -0
- package/knowledge/cyberpunk2077/vehicles.entities.yaml +195 -0
- package/knowledge/cyberpunk2077/vehicles.wiring.yaml +137 -0
- package/knowledge/cyberpunk2077/wolvenkit.cli.yaml +167 -0
- package/knowledge/cyberpunk2077/workspot.system.yaml +55 -0
- package/knowledge/cyberpunk2077/world.interactions.yaml +41 -0
- package/knowledge/cyberpunk2077/world.loot-container.yaml +167 -0
- package/knowledge/cyberpunk2077/world.sectors.yaml +233 -0
- package/knowledge/eldenring/formats.binders.yaml +237 -0
- package/knowledge/eldenring/launcher.me3.yaml +230 -0
- package/knowledge/eldenring/launcher.modengine2.yaml +119 -0
- package/knowledge/eldenring/params.paramdef.yaml +149 -0
- package/knowledge/eldenring/params.regulation.yaml +142 -0
- package/knowledge/eldenring/saves.online.yaml +118 -0
- package/knowledge/eldenring/toolchain.cli.yaml +256 -0
- package/knowledge/nivalisnights/modding.runtime.yaml +157 -0
- package/knowledge/skyrimse/archives.bsa.yaml +212 -0
- package/knowledge/skyrimse/install.detection.yaml +41 -0
- package/knowledge/skyrimse/loadorder.files.yaml +242 -0
- package/knowledge/skyrimse/plugins.esl.yaml +189 -0
- package/knowledge/skyrimse/plugins.format.yaml +224 -0
- package/knowledge/skyrimse/skse.runtime.yaml +283 -0
- package/knowledge/skyrimse/toolchain.xedit-spriggit.yaml +336 -0
- package/knowledge/stardewvalley/contentpatcher.format.yaml +338 -0
- package/knowledge/stardewvalley/distribution.channels.yaml +236 -0
- package/knowledge/stardewvalley/game.versions.yaml +202 -0
- package/knowledge/stardewvalley/install.layout.yaml +277 -0
- package/knowledge/stardewvalley/smapi.loader.yaml +284 -0
- package/knowledge/stardewvalley/smapi.logs.yaml +329 -0
- package/knowledge/stardewvalley/smapi.manifest.yaml +293 -0
- package/knowledge/stardewvalley/toolchain.build.yaml +261 -0
- package/knowledge/stardewvalley/wiki.dataformat.yaml +130 -0
- package/knowledge/subnautica2/game.build.yaml +331 -0
- package/knowledge/subnautica2/modding.state.yaml +210 -0
- package/knowledge/subnautica2/ue5.containers.yaml +72 -0
- package/knowledge/subnautica2/ue5.paks.yaml +203 -0
- package/knowledge/subnautica2/ue5.tooling.yaml +175 -0
- package/knowledge/subnautica2/ue5.ue4ss.yaml +203 -0
- package/knowledge/valheim/bepinex.loader.yaml +247 -0
- package/knowledge/valheim/bepinex.plugins.yaml +251 -0
- package/knowledge/valheim/game.versions.yaml +321 -0
- package/knowledge/valheim/install.layout.yaml +216 -0
- package/knowledge/valheim/jotunn.library.yaml +334 -0
- package/knowledge/valheim/thunderstore.packaging.yaml +217 -0
- package/knowledge/valheim/toolchain.build.yaml +111 -0
- package/package.json +29 -0
- package/scaffolds/baldursgate3/new-class-injection.yaml +148 -0
- package/scaffolds/baldursgate3/new-item.yaml +264 -0
- package/scaffolds/baldursgate3/new-mod.yaml +174 -0
- package/scaffolds/baldursgate3/new-passive.yaml +100 -0
- package/scaffolds/baldursgate3/new-project.yaml +118 -0
- package/scaffolds/baldursgate3/new-quest-stub.yaml +159 -0
- package/scaffolds/baldursgate3/new-spell.yaml +102 -0
- package/scaffolds/baldursgate3/new-status.yaml +119 -0
- package/scaffolds/baldursgate3/sequences/subclass-kit.yaml +50 -0
- package/scaffolds/cyberpunk2077/edit-tweak.yaml +70 -0
- package/scaffolds/cyberpunk2077/mesh-export-preset.yaml +170 -0
- package/scaffolds/cyberpunk2077/new-entity-patch.yaml +255 -0
- package/scaffolds/cyberpunk2077/new-garment-refit.yaml +279 -0
- package/scaffolds/cyberpunk2077/new-item-chain.yaml +548 -0
- package/scaffolds/cyberpunk2077/new-localization.yaml +130 -0
- package/scaffolds/cyberpunk2077/new-mod-settings.yaml +133 -0
- package/scaffolds/cyberpunk2077/new-player-replacer.yaml +1003 -0
- package/scaffolds/cyberpunk2077/new-project.yaml +157 -0
- package/scaffolds/cyberpunk2077/new-sector-node.yaml +156 -0
- package/scaffolds/cyberpunk2077/new-tweak.yaml +121 -0
- package/scaffolds/cyberpunk2077/new-vehicle-livery.yaml +148 -0
- package/scaffolds/cyberpunk2077/new-workspot-entity.yaml +177 -0
- package/scaffolds/cyberpunk2077/sequences/iconic-weapon.yaml +72 -0
- package/scaffolds/cyberpunk2077/sequences/quickhack-takeover.yaml +213 -0
- package/scaffolds/stardewvalley/new-cp-patch.yaml +106 -0
- package/scaffolds/stardewvalley/new-csharp-mod.yaml +195 -0
- package/scaffolds/stardewvalley/new-project.yaml +184 -0
- package/scaffolds/stardewvalley/sequences/content-pack.yaml +31 -0
- package/scripts/mw-tool.mjs +184 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# cp2077-cet bridge
|
|
2
|
+
|
|
3
|
+
The Cyberpunk 2077 Cyber Engine Tweaks bridge: ModWright's author-only verification mod. At
|
|
4
|
+
load it feature-detects the CET sandbox; when a request reaches it, it runs the probes that
|
|
5
|
+
request names and prints the result plus a `done` marker carrying the result's byte length. It
|
|
6
|
+
is staged by `stageBridge(project, "cyberpunk2077")` (`src/core/bridge/stage.ts`) into
|
|
7
|
+
`<project>/.modwright/bridge/cp2077-cet/`, and `deploy --variant withBridge` copies
|
|
8
|
+
`ModWrightBridge` from there into the project's `cet` root
|
|
9
|
+
(`bin/x64/plugins/cyber_engine_tweaks/mods/ModWrightBridge`).
|
|
10
|
+
|
|
11
|
+
The protocol is implemented on the Node side in `src/core/bridge/`.
|
|
12
|
+
|
|
13
|
+
## Two transports: files first, then console-armed and log-polled
|
|
14
|
+
|
|
15
|
+
An in-game check (2026-09-08) ran `io.open` in the CET **console**: `io` is nil there (CET
|
|
16
|
+
v1.37.1, game v3.0.80.51928). CET's changelog says that is by design and is not a fact about
|
|
17
|
+
mods: 1.10 "stripped ImGui, json, io and dofile and such from console sandbox", in the same
|
|
18
|
+
release that made mod pathing relative — "All pathing is now relative to mod (breaking change,
|
|
19
|
+
includes all io functions, dofile() and such included)" (CET Nexus changelog, mod 107). Mods
|
|
20
|
+
have `io`; the console does not. `cp2077.bridge.io` stays **unverified**, and the bridge's own
|
|
21
|
+
capability line (`m.where()`'s `io`, which rides in every heartbeat and lands in the mod's own
|
|
22
|
+
log) is what settles it. So:
|
|
23
|
+
|
|
24
|
+
* **The file transport below stays first choice.** Whenever the bridge reports a live write
|
|
25
|
+
root, `bridge arm` writes the request there and `bridge poll` reads the result files, exactly
|
|
26
|
+
as for BG3.
|
|
27
|
+
* **The console-and-log path is the fallback**, and it is what runs when the bridge
|
|
28
|
+
reports log-only. Both are tried on every arm and every poll, in that order.
|
|
29
|
+
|
|
30
|
+
Reading that capability line is the first thing to do on a new install — it needs no game
|
|
31
|
+
launch, because CET 1.9.1 ("Mods will now log to
|
|
32
|
+
their own log files") means it is already in the newest `.log` under
|
|
33
|
+
`bin/x64/plugins/cyber_engine_tweaks/mods/ModWrightBridge/`.
|
|
34
|
+
|
|
35
|
+
* **Arming** — `bridge arm --game cyberpunk2077 --path <mod repo>` builds the same `Request`
|
|
36
|
+
it would for BG3, writes it into the write root when there is one, and always returns the
|
|
37
|
+
console line that hands it to the bridge:
|
|
38
|
+
`GetMod("ModWrightBridge").arm('<compact request JSON>')`, escaped for a Lua single-quoted
|
|
39
|
+
string. It records the nonce under `<project>/.modwright/bridge/pending/<id>.json`; a dry
|
|
40
|
+
run shows the line and records nothing. The Lua needed no change for this —
|
|
41
|
+
`init.lua`'s `armFn` → `Protocol.armFromString` already accepts that payload verbatim.
|
|
42
|
+
* **Results** — a finished run writes `modwright_res_<id>.json` plus its `done` marker when it
|
|
43
|
+
can, and in log-only mode prints `[MODWRIGHT-RESULT] <json>` and then
|
|
44
|
+
`[MODWRIGHT-DONE] <json>` carrying the result's byte length. `bridge poll` checks the result
|
|
45
|
+
files first and then tails
|
|
46
|
+
the newest `.log` in the mod's own folder first (CET 1.9.1), then
|
|
47
|
+
`bin/x64/plugins/cyber_engine_tweaks/cyber_engine_tweaks.log` and `…/gamelog.log`, applies the same byte-length
|
|
48
|
+
completeness rule the file transport uses, matches the pending nonce, ignores lines older
|
|
49
|
+
than the arm, and says which file it read.
|
|
50
|
+
* **Heartbeat** — written to `modwright_hb.json` every 2 s, or printed as
|
|
51
|
+
`[MODWRIGHT-HEARTBEAT] <json>` every 15 s in log-only mode. `bridge status` reads the file
|
|
52
|
+
first and the log lines second, judges freshness against whichever answered (15 s / 45 s),
|
|
53
|
+
and reports **`sandboxIo`** — the bridge's own `io` capability, which is the fact the whole
|
|
54
|
+
transport question turns on.
|
|
55
|
+
* **`run_tests` can drive this game only when the sandbox has `io`.** It must run unattended:
|
|
56
|
+
against a file-writing bridge it arms and polls normally; against a log-only one it refuses
|
|
57
|
+
with `console-arm-required` naming the three-command flow instead.
|
|
58
|
+
|
|
59
|
+
### The `io` write-root discovery
|
|
60
|
+
|
|
61
|
+
Nothing on disk attests that CET's Lua sandbox has `io` at all (the first row of the probe
|
|
62
|
+
catalogue), so at `onInit` the bridge tries three candidates in order —
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
modwright_hb.json
|
|
66
|
+
mods/ModWrightBridge/modwright_hb.json
|
|
67
|
+
plugins/cyber_engine_tweaks/mods/ModWrightBridge/modwright_hb.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
— keeps the first that opens **and accepts a write**, and reports the winner in every
|
|
71
|
+
heartbeat's `writeRoot` with every failure string in `writeRootErrors`. ModWright polls the
|
|
72
|
+
matching absolute directories under the install (`bridgeCandidateDirs`,
|
|
73
|
+
`src/core/bridge/paths.ts`). If `io` turns out to be absent in the mod sandbox too, all three
|
|
74
|
+
fail before they are tried and the bridge falls into the log-only mode described above.
|
|
75
|
+
|
|
76
|
+
## protocolVersion
|
|
77
|
+
|
|
78
|
+
**1** — the current value of `PROTOCOL_VERSION` in `src/core/bridge/types.ts`. The Lua defines
|
|
79
|
+
the identical integer literal as `Protocol.PROTOCOL_VERSION` (`ModWrightBridge/protocol.lua`),
|
|
80
|
+
"`protocolVersion` is an integer duplicated in the Lua and the Node constant, checked at arm
|
|
81
|
+
time". `test/unit/bridge/cp2077-lua-parity.test.ts` asserts the two literals
|
|
82
|
+
agree.
|
|
83
|
+
|
|
84
|
+
## The tree
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
bridges/cp2077-cet/
|
|
88
|
+
bridge.json # id, protocolVersion, version, deploy target, file list, sha256s
|
|
89
|
+
README.md # this file
|
|
90
|
+
ModWrightBridge/
|
|
91
|
+
init.lua # registerForEvent onInit/onUpdate/onShutdown; returns the console table
|
|
92
|
+
protocol.lua # write-root discovery, JSON framing, nonce arming, heartbeat, run loop
|
|
93
|
+
probes.lua # the probe dispatch table
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Three files. `init.lua` is CET's entry point and **must
|
|
97
|
+
return a table** — knowledge fact `cp2077.process.cet-mods-sandboxed`, status `verified` on
|
|
98
|
+
2.31 — so that `GetMod("ModWrightBridge")` is reachable from the console. The CET mods surveyed
|
|
99
|
+
ship no manifest file, so none is invented.
|
|
100
|
+
|
|
101
|
+
**How `init.lua` loads the other two comes from CET's changelog, not from a sibling on disk.**
|
|
102
|
+
The CET mods surveyed have no second file. CET 1.10 made every mod path relative to the mod's
|
|
103
|
+
own folder and `require` paths `/`-separated, and 1.9.1 deprecated `dofile` without removing
|
|
104
|
+
it, so `init.lua` tries three routes in order — `require("protocol")`,
|
|
105
|
+
`require("ModWrightBridge/protocol")`, `dofile("protocol.lua")` — reports the winner in the
|
|
106
|
+
capability line, and, if all three fail, still returns a console table whose
|
|
107
|
+
`status()`/`where()` print the three error strings. A CET mod that returns nothing cannot even
|
|
108
|
+
be diagnosed.
|
|
109
|
+
|
|
110
|
+
## The dispatch table
|
|
111
|
+
|
|
112
|
+
Fenced with `-- MODWRIGHT-PROBE-TABLE-BEGIN` / `-- MODWRIGHT-PROBE-TABLE-END` in
|
|
113
|
+
`probes.lua`, so the parity test reads only the table and never an id that appears in a
|
|
114
|
+
comment. Thirteen ids, one per catalogue row, each carrying that row's own status word:
|
|
115
|
+
|
|
116
|
+
| id | Status | If its call is missing |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `cp2077.bridge.io` | unverified — the 2026-09-08 check ran in the console sandbox, which CET strips by design | `error` — and the bridge's own capability line says which case this install is in |
|
|
119
|
+
| `cp2077.mod.loaded` | attested(engine) | — |
|
|
120
|
+
| `cp2077.player.present` | attested(engine) | `error` if `Game` is absent |
|
|
121
|
+
| `cp2077.item.equipped` | attested(engine) | — |
|
|
122
|
+
| `cp2077.inventory.add` | attested(doc), **mutate** | `error`; skipped without `allowMutate` |
|
|
123
|
+
| `cp2077.entity.tagged` | attested(engine) | — |
|
|
124
|
+
| `cp2077.entity.components` | attested(engine) for the call; names unverified | `error` without `params.names` |
|
|
125
|
+
| `cp2077.entity.transform` | attested(engine) for item objects; unverified for a sector prop | says which, in `evidence.attestationForThisCall` |
|
|
126
|
+
| `cp2077.entity.appearance` | unverified (composite) | `skipped` without a baseline |
|
|
127
|
+
| `cp2077.tweakdb.record` | unverified | `error` |
|
|
128
|
+
| `cp2077.tweakdb.flat` | unverified | `error` |
|
|
129
|
+
| `cp2077.interaction.present` | unverified (the component *name*) | `error` without `params.names` |
|
|
130
|
+
| `cp2077.loot.roll` | **not designed** | always `unsupported` |
|
|
131
|
+
|
|
132
|
+
Every probe result carries its row's `evidence.attestation` and `evidence.citation`, so the
|
|
133
|
+
provenance of an answer travels with the answer.
|
|
134
|
+
|
|
135
|
+
## The content banner
|
|
136
|
+
|
|
137
|
+
`BRIDGE_BANNER` in `src/core/bridge/types.ts` (`"MODWRIGHT-BRIDGE-DO-NOT-SHIP"`) appears,
|
|
138
|
+
verbatim, as the first line of every `.lua` file here. This is what lets
|
|
139
|
+
`cp2077.history.bridge-never-ships` (`src/surfaces/cyberpunk2077/validators/bridge.ts`) catch a
|
|
140
|
+
bridge file that reached a build output under a path that no longer says "ModWrightBridge" —
|
|
141
|
+
e.g. flattened during packaging — not just one that still does.
|
|
142
|
+
|
|
143
|
+
## Checking the Lua
|
|
144
|
+
|
|
145
|
+
No Lua parser is vendored. Three checks:
|
|
146
|
+
|
|
147
|
+
* `npm run bridge:lint` — shells to `luac -p` under 5.1 and 5.4 when either is on PATH, and
|
|
148
|
+
prints a one-line skip when neither is.
|
|
149
|
+
* `test/unit/bridge/cp2077-lua-parity.test.ts` — reads the Lua as text: the dispatch-table keys
|
|
150
|
+
equal the catalogue's ids, every file carries the banner, the `PROTOCOL_VERSION` literals agree, and
|
|
151
|
+
`bridge.json` lists every file. It also runs `luac -p` when one is installed.
|
|
152
|
+
* The CI job installing `lua5.1`/`lua5.4`.
|
|
153
|
+
|
|
154
|
+
The Lua is kept inside the 5.1-compatible subset shipped CET mods use, and checked
|
|
155
|
+
against both dialects — CET embeds a 5.4-era sol2, but nothing on disk pins which dialect
|
|
156
|
+
features are available, so the intersection is the safe target.
|
|
157
|
+
|
|
158
|
+
## What this bridge never does
|
|
159
|
+
|
|
160
|
+
* It never observes: no CET listener API is attested, so `effect: "observe"` is `unsupported`
|
|
161
|
+
and `window.seconds` is ignored with a line in the result's `errors`.
|
|
162
|
+
* It never calls anything that mutates except `Game.AddToInventory`, and only when the request
|
|
163
|
+
sets `allowMutate: true`. `m.probe()` from the console always passes `allowMutate: false`.
|
|
164
|
+
* It never claims a `pass` for a call that is not there: an `unverified` row whose call is
|
|
165
|
+
missing returns `error` with a message naming what was missing and which check settles it.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "cp2077-cet",
|
|
3
|
+
"game": "cyberpunk2077",
|
|
4
|
+
"protocolVersion": 1,
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"modFolder": "ModWrightBridge",
|
|
7
|
+
"entryPoint": "ModWrightBridge/init.lua",
|
|
8
|
+
"banner": "MODWRIGHT-BRIDGE-DO-NOT-SHIP",
|
|
9
|
+
"transport": {
|
|
10
|
+
"primary": "file",
|
|
11
|
+
"fallback": "log",
|
|
12
|
+
"writeRootCandidates": [
|
|
13
|
+
"modwright_hb.json",
|
|
14
|
+
"mods/ModWrightBridge/modwright_hb.json",
|
|
15
|
+
"plugins/cyber_engine_tweaks/mods/ModWrightBridge/modwright_hb.json"
|
|
16
|
+
],
|
|
17
|
+
"note": "The write root is DISCOVERED at onInit: the first candidate whose `io.open(candidate, \"w\")` succeeds wins, and the winner is reported in every heartbeat's `writeRoot`. The first candidate is the empty prefix, which since CET 1.10 (\"All pathing is now relative to mod\", CET Nexus changelog) means this mod's own folder — the directory ModWright polls first. Whether any candidate resolves is still `unverified`: the `io.open` check of 2026-09-08 was typed in the CET console, whose sandbox has io stripped by design (same changelog), so it tested the wrong half. If none does, the bridge prints each result as one `[MODWRIGHT-RESULT] <json>` line for `triage_logs` to recover."
|
|
18
|
+
},
|
|
19
|
+
"deploy": {
|
|
20
|
+
"rootId": "cet",
|
|
21
|
+
"from": ".modwright/bridge/cp2077-cet/ModWrightBridge",
|
|
22
|
+
"into": "ModWrightBridge",
|
|
23
|
+
"variant": "withBridge",
|
|
24
|
+
"locked": false,
|
|
25
|
+
"backup": true,
|
|
26
|
+
"prune": [
|
|
27
|
+
"ModWrightBridge/**"
|
|
28
|
+
],
|
|
29
|
+
"note": "The deploy target `bridge stage` adds to the project's deploy targets when no target already names this rootId, into and variant, so a fresh project deploys the bridge without a hand edit; paste it in yourself only if you want it there before staging. A deploy WITHOUT `--variant withBridge` prunes the folder back out, with backup."
|
|
30
|
+
},
|
|
31
|
+
"sha256": {
|
|
32
|
+
"algorithm": "sha256",
|
|
33
|
+
"encoding": "hex",
|
|
34
|
+
"over": "the file's raw bytes, unmodified",
|
|
35
|
+
"paths": "POSIX-separated, relative to bridges/cp2077-cet/",
|
|
36
|
+
"regenerate": "python3 -c \"import hashlib,sys;print(hashlib.sha256(open(sys.argv[1],'rb').read()).hexdigest())\" <path>",
|
|
37
|
+
"note": "Recomputed by hand whenever a file here changes; `bridge stage` (src/core/bridge/stage.ts) copies the tree verbatim and does not fill these in, and nothing in ModWright reads them yet. They exist so a staged or deployed copy can be compared against the source of truth by hand."
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
{
|
|
41
|
+
"path": "ModWrightBridge/init.lua",
|
|
42
|
+
"sha256": "269528f9a81576f345ec1c679ace5331ae6ccfbb174d4d09efd09d3fb4fc91d8"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"path": "ModWrightBridge/probes.lua",
|
|
46
|
+
"sha256": "a9dc28d3fd949f353f17404366bd2bb329813b9ca639e06a9b51aa34c104fb7d"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"path": "ModWrightBridge/protocol.lua",
|
|
50
|
+
"sha256": "f4a59d07883ac9bcdb2da2d5d4cdcd3af42c322a40751515c551c9057ac24210"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { executePlan, planFile } from "../safety/index.js";
|
|
4
|
+
import { cetArmConsoleLine, cetArmSteps, compactRequestJson } from "./cet-console.js";
|
|
5
|
+
import { buildPendingArm, pendingArmFile, planPruneExpiredPendingArms, planRecordPendingArm } from "./pending.js";
|
|
6
|
+
import { bridgeFileNames } from "./paths.js";
|
|
7
|
+
import { PROTOCOL_VERSION, bridgeRequestSchema } from "./types.js";
|
|
8
|
+
import { bridgeStatus } from "./status.js";
|
|
9
|
+
export function generateNonce() {
|
|
10
|
+
return crypto.randomBytes(8).toString("hex");
|
|
11
|
+
}
|
|
12
|
+
export function generateRequestId() {
|
|
13
|
+
return `req-${Date.now().toString(36)}-${crypto.randomBytes(4).toString("hex")}`;
|
|
14
|
+
}
|
|
15
|
+
export function buildRequest(opts) {
|
|
16
|
+
const now = opts.now ?? (() => new Date());
|
|
17
|
+
const issuedAt = now();
|
|
18
|
+
const armForMs = (opts.armForSeconds ?? 60) * 1000;
|
|
19
|
+
const grant = opts.grant ?? (opts.allowMutate ? ["session"] : undefined);
|
|
20
|
+
const allowMutate = grant ? grant.includes("session") : opts.allowMutate ?? false;
|
|
21
|
+
const request = {
|
|
22
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
23
|
+
id: opts.id ?? generateRequestId(),
|
|
24
|
+
nonce: opts.nonce ?? generateNonce(),
|
|
25
|
+
issuedAt: issuedAt.toISOString(),
|
|
26
|
+
armedUntil: new Date(issuedAt.getTime() + armForMs).toISOString(),
|
|
27
|
+
game: opts.game,
|
|
28
|
+
allowMutate,
|
|
29
|
+
...(grant ? { grant } : {}),
|
|
30
|
+
...(opts.window ? { window: opts.window } : {}),
|
|
31
|
+
...(opts.plan ? { plan: opts.plan } : {}),
|
|
32
|
+
probes: opts.probes,
|
|
33
|
+
};
|
|
34
|
+
return bridgeRequestSchema.parse(request);
|
|
35
|
+
}
|
|
36
|
+
export function planArmRequest(dirAbs, request) {
|
|
37
|
+
const files = bridgeFileNames(request.game);
|
|
38
|
+
const reqPath = path.join(dirAbs, files.request);
|
|
39
|
+
const markerPath = path.join(dirAbs, files.requestMarker);
|
|
40
|
+
const reqJson = `${JSON.stringify(request, null, 2)}\n`;
|
|
41
|
+
const marker = {
|
|
42
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
43
|
+
id: request.id,
|
|
44
|
+
nonce: request.nonce,
|
|
45
|
+
bytes: Buffer.byteLength(reqJson, "utf8"),
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
purpose: `arm bridge request ${request.id} (${request.game})`,
|
|
49
|
+
operations: [planFile(reqPath, reqJson), planFile(markerPath, `${JSON.stringify(marker, null, 2)}\n`)],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function armBridge(input) {
|
|
53
|
+
const status = await bridgeStatus(input.game, input.install, input.surface, { now: input.now, readFile: input.readFile });
|
|
54
|
+
if (status.found && !status.protocolVersionMatch) {
|
|
55
|
+
return {
|
|
56
|
+
game: input.game,
|
|
57
|
+
transport: input.game === "cyberpunk2077" ? "console" : "file",
|
|
58
|
+
status,
|
|
59
|
+
refused: { reason: "protocol-mismatch", message: status.message },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (input.game === "cyberpunk2077")
|
|
63
|
+
return armCyberpunk(input, status);
|
|
64
|
+
const dir = status.dir ?? (status.candidates.length === 1 ? status.candidates[0] : undefined);
|
|
65
|
+
if (!dir) {
|
|
66
|
+
return {
|
|
67
|
+
game: input.game,
|
|
68
|
+
transport: "file",
|
|
69
|
+
status,
|
|
70
|
+
refused: {
|
|
71
|
+
reason: "unknown-write-root",
|
|
72
|
+
message: `No heartbeat was found and ${input.game} has more than one possible write root, so ModWright cannot tell ` +
|
|
73
|
+
`which one the bridge will use. Launch the game once with the bridge deployed and staged, then retry.`,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const now = input.now ?? (() => new Date());
|
|
78
|
+
const request = buildRequest(input);
|
|
79
|
+
const plan = planArmRequest(dir, request);
|
|
80
|
+
if (input.projectRoot) {
|
|
81
|
+
const armedAt = now();
|
|
82
|
+
plan.operations.push(...planRecordPendingArm(input.projectRoot, buildPendingArm(request, undefined, armedAt, input.window?.seconds)).operations, ...(await planPruneExpiredPendingArms(input.projectRoot, input.game, armedAt)));
|
|
83
|
+
}
|
|
84
|
+
const confidence = {
|
|
85
|
+
level: status.found ? "high" : "medium",
|
|
86
|
+
assumptions: status.found
|
|
87
|
+
? []
|
|
88
|
+
: [`No live heartbeat was seen; armed into the only possible directory for ${input.game} ahead of the bridge starting.`],
|
|
89
|
+
verifiedBy: status.found ? ["bridge heartbeat"] : [],
|
|
90
|
+
};
|
|
91
|
+
const write = await executePlan(input.projectRoot ? { ...plan, backupRoot: path.join(input.projectRoot, ".modwright", "backups") } : plan, { mode: input.mode ?? "dry-run", now: input.now, confidence });
|
|
92
|
+
return {
|
|
93
|
+
game: input.game,
|
|
94
|
+
transport: "file",
|
|
95
|
+
status,
|
|
96
|
+
dir,
|
|
97
|
+
request,
|
|
98
|
+
write,
|
|
99
|
+
...(input.projectRoot && (input.mode ?? "dry-run") === "apply" ? { pendingFile: pendingArmFile(input.projectRoot, request.id) } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
async function armCyberpunk(input, status) {
|
|
103
|
+
const now = input.now ?? (() => new Date());
|
|
104
|
+
const request = buildRequest(input);
|
|
105
|
+
const consoleLine = cetArmConsoleLine(request);
|
|
106
|
+
const fileDir = status.transport === "file" ? status.dir : undefined;
|
|
107
|
+
if (!fileDir && !input.console) {
|
|
108
|
+
return {
|
|
109
|
+
game: input.game,
|
|
110
|
+
transport: "console",
|
|
111
|
+
status,
|
|
112
|
+
request,
|
|
113
|
+
refused: {
|
|
114
|
+
reason: "console-arm-required",
|
|
115
|
+
message: `This bridge is not writing files ModWright can find (${status.sandboxIo === false ? "its capability line reports no io in the mod sandbox" : "no heartbeat file was found"}), ` +
|
|
116
|
+
`so the request only reaches it when a human pastes one CET console line. This caller cannot wait ` +
|
|
117
|
+
`for that. Run the "bridge" tool's "arm" action with the project path to get the line and record the nonce, ` +
|
|
118
|
+
`paste it into the CET console, then "bridge poll".`,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (!fileDir && !input.projectRoot) {
|
|
123
|
+
return {
|
|
124
|
+
game: input.game,
|
|
125
|
+
transport: "console",
|
|
126
|
+
status,
|
|
127
|
+
request,
|
|
128
|
+
refused: {
|
|
129
|
+
reason: "no-project-root",
|
|
130
|
+
message: `A console-armed Cyberpunk request records its pending nonce under <project>/.modwright/bridge/pending/ so ` +
|
|
131
|
+
`"bridge poll" can match the result lines in the CET log. Pass the mod project's path.`,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const armedAt = now();
|
|
136
|
+
const mode = input.mode ?? "dry-run";
|
|
137
|
+
const operations = [
|
|
138
|
+
...(fileDir ? planArmRequest(fileDir, request).operations : []),
|
|
139
|
+
...(input.projectRoot
|
|
140
|
+
? planRecordPendingArm(input.projectRoot, buildPendingArm(request, consoleLine, armedAt, input.window?.seconds)).operations
|
|
141
|
+
: []),
|
|
142
|
+
...(input.projectRoot ? await planPruneExpiredPendingArms(input.projectRoot, input.game, armedAt) : []),
|
|
143
|
+
];
|
|
144
|
+
const confidence = {
|
|
145
|
+
level: status.found ? "high" : "medium",
|
|
146
|
+
assumptions: [
|
|
147
|
+
...(fileDir
|
|
148
|
+
? [`The bridge reports a live write root, so the request was written there as well; if it never picks it up, paste the console line instead.`]
|
|
149
|
+
: [`The request reaches the bridge only when a human pastes the console line; ModWright cannot confirm that it was run.`]),
|
|
150
|
+
...(status.found
|
|
151
|
+
? []
|
|
152
|
+
: ["No heartbeat was found in either transport, so the bridge may not be loaded yet — the console line can still be pasted once it is."]),
|
|
153
|
+
],
|
|
154
|
+
verifiedBy: status.found ? [`bridge heartbeat (${status.transport} transport)`] : [],
|
|
155
|
+
};
|
|
156
|
+
const write = await executePlan({
|
|
157
|
+
purpose: `arm bridge request ${request.id} (cyberpunk2077, ${fileDir ? "file + console" : "console"})`,
|
|
158
|
+
operations,
|
|
159
|
+
...(input.projectRoot ? { backupRoot: path.join(input.projectRoot, ".modwright", "backups") } : {}),
|
|
160
|
+
}, { mode, now: input.now, confidence });
|
|
161
|
+
return {
|
|
162
|
+
game: input.game,
|
|
163
|
+
transport: fileDir ? "file" : "console",
|
|
164
|
+
status,
|
|
165
|
+
...(fileDir ? { dir: fileDir } : {}),
|
|
166
|
+
request,
|
|
167
|
+
write,
|
|
168
|
+
console: {
|
|
169
|
+
consoleLine,
|
|
170
|
+
requestJson: compactRequestJson(request),
|
|
171
|
+
...(mode === "apply" && input.projectRoot ? { pendingFile: pendingArmFile(input.projectRoot, request.id) } : {}),
|
|
172
|
+
steps: cetArmSteps(request, mode === "apply" && input.projectRoot ? pendingArmFile(input.projectRoot, request.id) : undefined, Boolean(fileDir)),
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CET_DONE_TAG, CET_RESULT_TAG } from "./cet-log.js";
|
|
2
|
+
export const CET_CONSOLE_MOD = "ModWrightBridge";
|
|
3
|
+
export function compactRequestJson(request) {
|
|
4
|
+
return JSON.stringify(request).replace(/[\u007f-\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
5
|
+
}
|
|
6
|
+
export function luaSingleQuoted(value) {
|
|
7
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
8
|
+
}
|
|
9
|
+
export function cetArmConsoleLine(request) {
|
|
10
|
+
return `GetMod("${CET_CONSOLE_MOD}").arm('${luaSingleQuoted(compactRequestJson(request))}')`;
|
|
11
|
+
}
|
|
12
|
+
export function cetArmSteps(request, pendingFile, alsoWrittenAsFile = false) {
|
|
13
|
+
return [
|
|
14
|
+
...(alsoWrittenAsFile
|
|
15
|
+
? [
|
|
16
|
+
`The request was also written as a file into the write root this bridge reported, so it may be picked up on its own within a second — poll first, and only paste the line below if nothing comes back.`,
|
|
17
|
+
]
|
|
18
|
+
: []),
|
|
19
|
+
`Open the CET console (~) with a save loaded and paste the line in "consoleLine" exactly as it is, on one line.`,
|
|
20
|
+
`The bridge prints "[ModWrightBridge] armed by hand" and then, when the probes finish, one ${CET_RESULT_TAG} line ` +
|
|
21
|
+
`followed by one ${CET_DONE_TAG} line.`,
|
|
22
|
+
`Close the console if any probe reads pose, animation or equipment: the weapon drops into the holstered pose while the console is open.`,
|
|
23
|
+
`Then run bridge poll — id "${request.id}"${pendingFile ? ` (recorded in ${pendingFile})` : ""} — which tails the CET log for those two lines.`,
|
|
24
|
+
`The request stays armed until ${request.armedUntil}; after that the bridge refuses it with disarm.reason "expired".`,
|
|
25
|
+
];
|
|
26
|
+
}
|