Commit Graph
6 Commits
Author SHA1 Message Date
Patryk GenschandClaude Opus 5 285b4ed316 Drop JAVA_TOOL_OPTIONS and --system from the image
JAVA_TOOL_OPTIONS made the JVM print "Picked up ..." on every invocation, and
the entrypoint already sets file.encoding through JAVA_OPTS. useradd --system
warned because the uid is above SYS_UID_MAX; the explicit uid is what matters.

Verified end to end on Docker 29.4: image builds, the full pipeline runs
inside the container against a read-only collection mount, the frontend and
MCP answer on the published port, and the database survives a restart in the
named volume. The read-only mount was confirmed to actually reject writes,
and the container runs as uid 10001, not root.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:51:39 +02:00
Patryk GenschandClaude Opus 5 02988297ec Add web frontend and Docker packaging
Frontend is a single page served from classpath resources with a JSON API
alongside it. Kept separate from the MCP tools because the consumers differ:
a model reads formatted text, a browser needs structures. Only the database
is shared.

The page has three views - catalog with edition details, copies, detected
facts with evidence and a filterable file list; script search with FTS5
snippets and a viewer; and a format breakdown. No build step, no CDN, no
dependencies; light and dark follow the system.

New `serve` command puts the frontend and MCP on one port, one process and
one database connection, which is also what the container runs. `mcp` alone
still works for a headless setup.

Docker is a two-stage build: JDK plus the Rex-EMoolator submodule to compile,
JRE for runtime. installDist rather than build, so check - and with it
verifyCoreVersion - is skipped and git is not needed in the image. The pinned
:core tag is extracted from gradle.properties at build time and passed in by
the entrypoint, otherwise copy.core_version would record "nieznana" and
derived-artifact invalidation would stop working. Properties go through
JAVA_OPTS because Gradle's launcher treats everything after the script name
as application arguments.

Compose mounts the collection read-only and keeps the database and script
cache in a named volume. The port is bound to loopback.

Not verified: the image itself does not build here, the Docker daemon is not
running on this machine. The launcher, JAVA_OPTS handling and resource
packaging were tested against the installDist output directly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:34:51 +02:00
Patryk GenschandClaude Opus 5 393bcde6a8 Add MCP server over Streamable HTTP
Seven read-only tools over the catalog database: list_titles, search_scripts,
get_script, get_edition, list_files, find_file and collection_stats. Writing
stays in the CLI, where the effect of a command is visible.

Transport is Streamable HTTP on the JDK's com.sun.net.httpserver, so Gson is
the only new dependency. No sessions are kept: every tool is stateless, so
Mcp-Session-Id is omitted and GET returns 405 rather than opening an SSE
stream we would never write to. Requests carrying a non-loopback Origin are
rejected, since a page in a browser can POST to localhost.

Tool results are formatted text rather than JSON. The consumer is a model
reading the answer, and prose costs less context than the same data wrapped
in objects.

Required arguments are validated against each tool's inputSchema before
dispatch, so a missing parameter is an explicit tool error instead of a
result computed from a default. find_file groups by the canonical lowercase
path, otherwise an extracted directory and its ISO look like two files.

Database access is serialized on one lock because sqlite-jdbc shares a single
connection; the workload is read-only and single-client.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:25:36 +02:00
Patryk GenschandClaude Opus 5 2a051c561b Add script decoding, full-text search and catalog entities
Script pipeline:
- decode: ScriptDecypher from :core into a content-addressed cache keyed by
  blob SHA-1, so a script shared by several images is decoded once. Cache
  invalidation is driven by artifact.tool_version, i.e. the pinned :core tag.
- find/cat: FTS5 index over decoded bodies. tokenchars '_' keeps identifiers
  whole; a query that fails to parse as FTS5 is retried as a quoted phrase.

Catalog entities:
- analyze: MetadataDetector reads dane/application.def for build date, game
  version, engine version and episodes. The APPLICATION object is located by
  type, not by name, since it is GAME, UFO or PIRACI depending on the title.
  CREATIONTIME is recorded separately from release_date because it is the
  project creation date, shared across a whole series.
- promote: builds titles and editions. KnownHashes entries conflate levels
  ("Reksio i UFO (pierwsza wersja)" is title plus edition label), so the
  parenthetical is split off and both UFO releases land under one title.
  Editions are merged on a fingerprint of engine DLL plus application.def
  hash; the DLL alone cannot separate the Herkules/Odyseusz two-in-one disc.
- set/lang: manual metadata a detector cannot infer - provenance, language
  lists with roles, engine/compiler/date overrides. Re-running promote only
  touches mechanical fields and leaves curated ones intact.

Schema:
- edition is rebuilt: dll_sha1 loses UNIQUE, since the two-in-one disc shares
  one engine library across two games. fingerprint becomes the merge key and
  *_override columns hold curated values. The rebuild only runs on an empty
  table; otherwise it fails loudly rather than dropping curated data.
- script_fts, an FTS5 virtual table keyed by blob rather than by file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:17:52 +02:00
Patryk Gensch aaf2073ff7 Update Rex-EMoolator version 2026-08-20 21:40:10 +02:00
Patryk Gensch fd21d8c9a6 Initialized project with game ingesting 2026-08-11 00:31:56 +02:00