{"files_list":{"files_list":[{"path":"project.pax","content":"---\r\nPAX Project Configuration\r\n*---\r\n\/\r\n    name        :: \"pax\",\r\n    version     :: \"4.4.0\",\r\n    author      :: \"xcx-official\",\r\n    description :: \"PAX project\",\r\n    tags  :: [],\r\n    files :: [],\r\n    deps  :: [\r\n\r\n    ]\r\n\/\r\n"},{"path":"README.md","content":"# PAX Package Manager Manual\r\n\r\nPAX is the official package manager for XCX, integrated directly into the `xcx` binary. It manages dependencies, project scaffolding, registry publishing, and build automation.\r\n\r\n---\r\n\r\n## Project Configuration: `project.pax`\r\n\r\nEvery PAX project must have a `project.pax` file in its root directory. It uses a custom declarative format.\r\n\r\n```pax\r\n---\r\nPAX Project Configuration\r\n*---\r\n\/\r\n    name        :: \"my_project\",\r\n    version     :: \"1.0.0\",\r\n    author      :: \"yourname\",\r\n    description :: \"A short description of the project\",\r\n    main        :: \"src\/main.xcx\",\r\n    tags  :: [\"tag1\", \"tag2\"],\r\n    files :: [\"src\/main.xcx\", \"src\/lib.xcx\"],\r\n    deps  :: [\r\n        \"somelib@1.0.0\",\r\n        \"author\/repo@latest\",\r\n        \"https:\/\/example.com\/lib.xcx\"\r\n    ]\r\n\/\r\n```\r\n\r\n### Fields\r\n\r\n| Field         | Required | Description                                                                 |\r\n|---------------|----------|-----------------------------------------------------------------------------|\r\n| `name`        | Yes      | Logical name of the project\/package.                                        |\r\n| `version`     | Yes      | Semantic version string (e.g. `\"1.0.0\"`).                                  |\r\n| `author`      | No       | Author name, shown on the registry.                                         |\r\n| `description` | No       | Short description of the project.                                           |\r\n| `main`        | No       | Custom entry point. Defaults to `src\/main.xcx` if omitted.                 |\r\n| `tags`        | No       | List of tags for registry discoverability.                                  |\r\n| `files`       | No       | Explicit list of files to include when publishing. If omitted, PAX excludes known non-code files automatically. |\r\n| `deps`        | No       | List of dependencies. Supports versioned names, `author\/repo` shortcuts, and direct URLs. |\r\n\r\n### Dependency Formats\r\n\r\n```pax\r\ndeps :: [\r\n    \"mylib@1.0.0\",           --- versioned registry package\r\n    \"mylib@latest\",          --- latest registry package\r\n    \"author\/mylib@2.1.0\",    --- with author prefix\r\n    \"https:\/\/example.com\/lib.xcx\"  --- direct URL\r\n]\r\n```\r\n\r\n---\r\n\r\n## Lock File: `pax.lock`\r\n\r\nPAX automatically maintains a `pax.lock` file after each install. It records the exact resolved version and local path of every installed dependency.\r\n\r\n```json\r\n{\r\n  \"mylib\": { \"version\": \"mylib@1.0.0\", \"file\": \"lib\/mylib\/\" },\r\n  \"otherlib\": { \"version\": \"otherlib@latest\", \"file\": \"lib\/otherlib\/\" }\r\n}\r\n```\r\n\r\n- Do **not** edit `pax.lock` manually.\r\n- Commit it to version control for reproducible builds.\r\n\r\n---\r\n\r\n## Directory Structure\r\n\r\nA standard PAX project follows this layout:\r\n\r\n```\r\nmy_project\/\r\n\u251c\u2500\u2500 project.pax       # Project configuration\r\n\u251c\u2500\u2500 pax.lock          # Auto-generated lockfile\r\n\u251c\u2500\u2500 src\/\r\n\u2502   \u2514\u2500\u2500 main.xcx      # Main entry point\r\n\u251c\u2500\u2500 lib\/              # Installed dependencies (managed by PAX)\r\n\u2514\u2500\u2500 tests\/            # Project-specific tests\r\n```\r\n\r\n---\r\n\r\n## Command Reference\r\n\r\nPAX is invoked via `xcx pax <command>`.\r\n\r\n| Command                    | Description                                            |\r\n|----------------------------|--------------------------------------------------------|\r\n| `xcx pax new <name>`       | Scaffold a new project structure.                      |\r\n| `xcx pax install`          | Fetch all dependencies into `lib\/`.                    |\r\n| `xcx pax add <dep>`        | Add a dependency and install it immediately.           |\r\n| `xcx pax remove <name>`    | Remove a dependency from `project.pax`.                |\r\n| `xcx pax clone <package>`  | Download a published package as a local project.       |\r\n| `xcx pax run [path]`       | Execute the project entry point.                       |\r\n| `xcx pax search <query>`   | Search the registry for available packages.            |\r\n| `xcx pax upgrade <target>` | Upgrade the local XCX installation (xcx, tools, or all).|\r\n| `xcx pax login <token>`    | Store a registry authentication token.                 |\r\n| `xcx pax logout`           | Remove the stored authentication token.                |\r\n| `xcx pax whoami`           | Verify your current registry account.                  |\r\n| `xcx pax publish`          | Publish the project to the PAX registry.               |\r\n\r\n---\r\n\r\n## Command Details\r\n\r\n### `xcx pax new <name>`\r\n\r\nScaffolds a new project with the standard directory layout.\r\n\r\n```sh\r\nxcx pax new my_project\r\n```\r\n\r\nCreates:\r\n```\r\nmy_project\/\r\n\u251c\u2500\u2500 project.pax\r\n\u251c\u2500\u2500 src\/\r\n\u2502   \u2514\u2500\u2500 main.xcx\r\n\u2514\u2500\u2500 README.md\r\n```\r\n\r\n---\r\n\r\n### `xcx pax install`\r\n\r\nReads `project.pax`, resolves all `deps`, and downloads them into `lib\/`. Skips packages already present (by name).\r\n\r\n```sh\r\nxcx pax install\r\n```\r\n\r\n- Only installs files declared in the dependency's own `files` field.\r\n- If the dependency has no `files` field, PAX automatically excludes `README.md`, `LICENSE`, `tests\/`, `docs\/`, `.git*`, and `project.pax`.\r\n- Updates `pax.lock` after each successful fetch.\r\n\r\n---\r\n\r\n### `xcx pax add <dep>`\r\n\r\nAdds a dependency to `project.pax` and immediately runs install.\r\n\r\n```sh\r\nxcx pax add mylib@1.0.0\r\nxcx pax add author\/mylib@latest\r\n```\r\n\r\n---\r\n\r\n### `xcx pax remove <name>`\r\n\r\nRemoves a dependency entry from `project.pax`.\r\n\r\n```sh\r\nxcx pax remove mylib\r\n```\r\n\r\n> **Note:** `lib\/` folders and `pax.lock` entries are not automatically cleaned up. Re-run `xcx pax install` or remove them manually.\r\n\r\n---\r\n\r\n### `xcx pax clone <package>`\r\n\r\nDownloads a published package from the PAX registry as a full local project, ready to run or modify.\r\n\r\n```sh\r\nxcx pax clone snake_game\r\nxcx pax clone author\/snake_game\r\n```\r\n\r\n- Creates a directory named after the package in the current working directory.\r\n- Copies the full project structure including `project.pax`, `src\/`, and all declared files.\r\n- Does **not** install dependencies automatically.\r\n\r\n**Typical workflow:**\r\n```sh\r\nxcx pax clone snake_game\r\ncd snake_game\r\nxcx pax install\r\nxcx pax run\r\n```\r\n\r\n---\r\n\r\n### `xcx pax run [path]`\r\n\r\nExecutes the project. Entry point resolution order:\r\n\r\n1. Path provided as argument (e.g. `xcx pax run other\/main.xcx`)\r\n2. `main` field in `project.pax`\r\n3. Default: `src\/main.xcx`\r\n\r\n```sh\r\nxcx pax run\r\nxcx pax run src\/alt.xcx\r\n```\r\n\r\n---\r\n\r\n### `xcx pax search <query>`\r\n\r\nSearches the PAX registry for packages matching the query.\r\n\r\n```sh\r\nxcx pax search json\r\n```\r\n\r\nOutput format:\r\n```\r\n- json_utils (v1.2.0) by alice\r\n- fast_json (v0.9.1) by bob\r\n```\r\n\r\n---\r\n\r\n### `xcx pax upgrade <target>`\r\n\r\nUpgrades the local XCX installation itself, separate from project dependency management. Pulls release artifacts from the official GitHub releases.\r\n\r\n```sh\r\nxcx pax upgrade xcx      --- upgrade only the xcx binary (compiler\/VM\/JIT, includes REPL)\r\nxcx pax upgrade tools    --- upgrade only pax, stdlib, and docs (installed under lib\/ next to the binary)\r\nxcx pax upgrade all      --- upgrade both xcx and tools\r\n```\r\n\r\n`<target>` is required. Running `xcx pax upgrade` with no target prints an error:\r\n\r\n```\r\nError: specify what to upgrade: xcx, tools, all\r\n```\r\n\r\n#### `--check`\r\n\r\nAdd `--check` to any of the three targets to only compare the local version against the latest GitHub release tag and print the result, without downloading or installing anything.\r\n\r\n```sh\r\nxcx pax upgrade xcx --check\r\nxcx pax upgrade tools --check\r\nxcx pax upgrade all --check\r\n```\r\n\r\n#### Install layout\r\n\r\n`tools` (pax, stdlib, docs) installs into `lib\/` next to the `xcx` binary itself \u2014 this is a global, installation-level directory, distinct from a project's own `lib\/` (which holds dependencies declared in that project's `project.pax`). Upgrading `tools` does not touch project-level dependencies.\r\n\r\n```\r\n<xcx install directory>\/\r\n\u251c\u2500\u2500 xcx          --- binary (source)\r\n\u2514\u2500\u2500 lib\/         --- pax, stdlib, docs (tools)\r\n```\r\n\r\n> **Note:** Downgrading to a specific older version is not currently supported.\r\n\r\n---\r\n\r\n### `xcx pax login <token>`\r\n\r\nStores a registry authentication token locally in `.pax_token`.\r\n\r\n```sh\r\nxcx pax login your_token_here\r\n```\r\n\r\n---\r\n\r\n### `xcx pax logout`\r\n\r\nRemoves the stored authentication token.\r\n\r\n```sh\r\nxcx pax logout\r\n```\r\n\r\n---\r\n\r\n### `xcx pax whoami`\r\n\r\nVerifies your current registry session and prints your account info.\r\n\r\n```sh\r\nxcx pax whoami\r\n# Account: alice [developer]\r\n```\r\n\r\n---\r\n\r\n### `xcx pax publish`\r\n\r\nPublishes the current project to the PAX registry. Requires login.\r\n\r\n```sh\r\nxcx pax publish\r\n```\r\n\r\n- Reads `project.pax` for metadata (`name`, `version`, `description`).\r\n- Scans and uploads all project files (excluding `.zip`, `.pax_token`, `.git`).\r\n- Sends a compressed archive + file manifest to the registry.\r\n\r\n> **Note:** Bump `version` in `project.pax` before each publish. The registry may reject duplicate versions.\r\n\r\n---\r\n\r\n## Registry Configuration\r\n\r\nBy default PAX connects to `pax.xcxlang.com`. You can override this via a `.pax_config` file in the project root:\r\n\r\n```json\r\n{\r\n  \"registry\": \"https:\/\/my-custom-registry.example.com\"\r\n}\r\n```\r\n\r\n- HTTP is used automatically for `localhost` and `127.0.0.1`.\r\n- HTTPS is used for all other hosts."},{"path":"src\/pax.xcx","content":"--- pax.xcx - PAX Package Manager for XCX v4.4\r\nconst s: REGISTRY_HOST = \"pax.xcxlang.com\";\r\nconst s: TOKEN_FILE    = \".pax_token\";\r\nconst s: CONFIG_FILE   = \".pax_config\";\r\n\r\nfunc getRegistryUrl(-> s) {\r\n    s: host = REGISTRY_HOST;\r\n    if (store.exists(CONFIG_FILE)) then;\r\n        s: content = store.read(CONFIG_FILE);\r\n        if (content.startsWith(\"{\")) then;\r\n            json: cfg = json.parse(content);\r\n            if (cfg.exists(\"registry\")) then;\r\n                cfg.bind(\"registry\", host);\r\n            end;\r\n        end;\r\n    end;\r\n\r\n    s: url = \"\";\r\n    if (host.startsWith(\"http\")) then;\r\n        url = host;\r\n    else;\r\n        if (host.startsWith(\"localhost\") OR host.startsWith(\"127.0.0.1\")) then;\r\n            url = \"http:\/\/\" + host;\r\n        else;\r\n            url = \"https:\/\/\" + host;\r\n        end;\r\n    end;\r\n    \r\n    if (NOT url.endsWith(\"\/\")) then;\r\n        url = url + \"\/\";\r\n    end;\r\n    \r\n    return url;\r\n};\r\n\r\nfunc main() {\r\n    array:s: args = env.args();\r\n    \r\n    if (args.size() < 3) then;\r\n        >! \"PAX Package Manager v4.2\";\r\n        >! \"Usage: xcx pax <command> [args]\";\r\n        >! \"\";\r\n        >! \"Core Commands:\";\r\n        >! \"  new <n>      Create a new project structure\";\r\n        >! \"  install         Install dependencies (minimum library setup)\";\r\n        >! \"  clone <n>    Clone package structure (full repository)\";\r\n        >! \"  add <n>      Add a package (name, URL, or author\/repo)\";\r\n        >! \"  remove <n>   Remove a package and update lockfile\";\r\n        >! \"  search <q>      Search for packages on the Registry\";\r\n        >! \"  run [path]      Execute the project entry point\";\r\n        >! \"\";\r\n        >! \"Registry Commands:\";\r\n        >! \"  login <token>   Store registry authentication token\";\r\n        >! \"  logout          Remove stored tokens\";\r\n        >! \"  whoami          Verify your registry account\";\r\n        >! \"  publish         Publish project and all files to Registry\";\r\n        >! \"  upgrade <t>     Upgrade target (xcx, tools, all) [--check]\";\r\n        return;\r\n    end;\r\n    \r\n    s: command = args.get(2);\r\n    \r\n    if (command == \"new\") then;\r\n        paxNew(args);\r\n    elif (command == \"install\") then;\r\n        paxInstall(args);\r\n    elif (command == \"clone\") then;\r\n        paxClone(args);\r\n    elif (command == \"add\") then;\r\n        paxAdd(args);\r\n    elif (command == \"remove\") then;\r\n        paxRemove(args);\r\n    elif (command == \"search\") then;\r\n        paxSearch(args);\r\n    elif (command == \"run\") then;\r\n        paxRun(args);\r\n    elif (command == \"login\") then;\r\n        paxLogin(args);\r\n    elif (command == \"logout\") then;\r\n        paxLogout();\r\n    elif (command == \"whoami\") then;\r\n        paxWhoAmI();\r\n    elif (command == \"publish\") then;\r\n        paxPublish();\r\n    elif (command == \"upgrade\") then;\r\n        paxUpgrade(args);\r\n    else;\r\n        >! \"Error: Unknown command '\" + command + \"'\";\r\n    end;\r\n};\r\n\r\n--- ================================================================\r\n---   Authentication Helpers\r\n--- ================================================================\r\n\r\nfunc saveToken(s: token) {\r\n    store.write(TOKEN_FILE, token);\r\n    >! \"Authentication success! Token saved.\";\r\n};\r\n\r\nfunc getToken(-> s) {\r\n    if (store.exists(TOKEN_FILE)) then;\r\n        return store.read(TOKEN_FILE).trim();\r\n    end;\r\n    return \"\";\r\n};\r\n\r\nfunc paxLogin(array:s: args) {\r\n    if (args.size() < 4) then;\r\n        >! \"Usage: xcx pax login <token>\";\r\n        return;\r\n    end;\r\n    saveToken(args.get(3));\r\n};\r\n\r\nfunc paxLogout() {\r\n    if (store.exists(TOKEN_FILE)) then;\r\n        store.delete(TOKEN_FILE);\r\n        >! \"Session terminated. Token removed.\";\r\n    else;\r\n        >! \"No active session found.\";\r\n    end;\r\n};\r\n\r\nfunc paxWhoAmI() {\r\n    s: token = getToken();\r\n    if (token == \"\") then;\r\n        >! \"Error: Not logged in. Use 'xcx pax login <token>'.\";\r\n        return;\r\n    end;\r\n    \r\n    map: authHeaders {\r\n        schema = [s <-> s]\r\n        data = [\"X-PAX-TOKEN\" :: token]\r\n    };\r\n    \r\n    net.request {\r\n        method  = \"GET\",\r\n        url     = getRegistryUrl() + \"api?route=api\/whoami\",\r\n        headers = authHeaders,\r\n        timeout = 5000,\r\n    } as resp;\r\n    \r\n    if (resp.ok) then;\r\n        s: body = s(resp.body);\r\n        if (body.startsWith(\"{\")) then;\r\n            json: data = resp.body;\r\n            if (data.exists(\"username\")) then;\r\n                s: user; data.bind(\"username\", user);\r\n                s: role; data.bind(\"role\", role);\r\n                >! \"Account: \" + user + \" [\" + role + \"]\";\r\n            else;\r\n                >! \"Error: Registry returned unexpected JSON format.\";\r\n            end;\r\n        else;\r\n            >! \"Error: Registry returned non-JSON response: \" + body;\r\n        end;\r\n    else;\r\n        >! \"Auth Error: Status \" + s(resp.status);\r\n        if (resp.exists(\"error\")) then; >! \"Details: \" + s(resp.get(\"error\")); end;\r\n    end;\r\n};\r\n\r\n--- ================================================================\r\n---   Configuration Helpers (PAX\/JSON Mapping)\r\n--- ================================================================\r\n\r\nfunc parsePax(s: content -> json) {\r\n    array:s: lines = content.split(\"\\n\");\r\n    json: res <<< {\"name\": \"unnamed\", \"version\": \"0.1.0\", \"author\": \"\", \"description\": \"\", \"main\": \"\", \"deps\": [], \"tags\": [], \"files\": []} >>>;\r\n    \r\n    s: cK = \"\";\r\n    for line in lines do;\r\n        s: l = line.split(\"---\").get(0).trim();\r\n        if (l == \"\" OR l == \"\/\") then; continue; end;\r\n\r\n        if (l HAS \"::\") then;\r\n            array:s: kv = l.split(\"::\");\r\n            if (kv.size() >= 2) then;\r\n                s: k = kv.get(0).trim().lower();\r\n                s: v = kv.get(1).trim();\r\n\r\n                if (v HAS \"\\\"\") then;\r\n                    array:s: b = v.split(\"\\\"\");\r\n                    if (b.size() >= 2) then;\r\n                        s: val = b.get(1);\r\n                        if (k == \"name\") then; res.set(\"name\", val);\r\n                        elif (k == \"version\") then; res.set(\"version\", val);\r\n                        elif (k == \"author\") then; res.set(\"author\", val);\r\n                        elif (k == \"description\") then; res.set(\"description\", val);\r\n                        elif (k == \"main\") then; res.set(\"main\", val);\r\n                        elif (k == \"files\") then; res.get(\"files\").push(val);\r\n                        end;\r\n                    end;\r\n                end;\r\n\r\n                if (v HAS \"[\") then;\r\n                    cK = k;\r\n                    if (v HAS \"]\") then;\r\n                        array:s: bits; bits = v.split(\"\\\"\");\r\n                        i: idx = 1;\r\n                        while (idx < bits.size()) do;\r\n                            if (cK == \"deps\") then; res.get(\"deps\").push(bits.get(idx));\r\n                            elif (cK == \"tags\") then; res.get(\"tags\").push(bits.get(idx));\r\n                            elif (cK == \"files\") then; res.get(\"files\").push(bits.get(idx));\r\n                            end;\r\n                            idx = idx + 2;\r\n                        end;\r\n                        cK = \"\";\r\n                    end;\r\n                end;\r\n            end;\r\n        elif (cK != \"\") then;\r\n            if (l HAS \"\\\"\") then;\r\n                array:s: bits; bits = l.split(\"\\\"\");\r\n                i: idx = 1;\r\n                while (idx < bits.size()) do;\r\n                    if (cK == \"deps\") then; res.get(\"deps\").push(bits.get(idx));\r\n                    elif (cK == \"tags\") then; res.get(\"tags\").push(bits.get(idx));\r\n                    elif (cK == \"files\") then; res.get(\"files\").push(bits.get(idx));\r\n                    end;\r\n                    idx = idx + 2;\r\n                end;\r\n            end;\r\n            if (l HAS \"]\") then; cK = \"\"; end;\r\n        end;\r\n    end;\r\n    return res;\r\n};\r\n\r\nfunc toPax(json: cfg -> s) {\r\n    s: name; cfg.bind(\"name\", name);\r\n    s: ver;  cfg.bind(\"version\", ver);\r\n    s: auth; cfg.bind(\"author\", auth);\r\n    s: desc; cfg.bind(\"description\", desc);\r\n    array:s: deps = cfg.get(\"deps\");\r\n    array:s: tags = cfg.get(\"tags\");\r\n    array:s: filesX = cfg.get(\"files\");\r\n    \r\n    s: res = \"---\\nPAX Project Configuration\\n*---\\n\/\\n\";\r\n    res = res + \"    name        :: \\\"\" + name + \"\\\",\\n\";\r\n    res = res + \"    version     :: \\\"\" + ver + \"\\\",\\n\";\r\n    res = res + \"    author      :: \\\"\" + auth + \"\\\",\\n\";\r\n    res = res + \"    description :: \\\"\" + desc + \"\\\",\\n\";\r\n    \r\n    if (cfg.exists(\"main\")) then;\r\n        s: main_entry; cfg.bind(\"main\", main_entry);\r\n        if (main_entry != \"\") then;\r\n            res = res + \"    main        :: \\\"\" + main_entry + \"\\\",\\n\";\r\n        end;\r\n    end;\r\n    \r\n    res = res + \"    tags  :: \" + tags.toStr() + \",\\n\";\r\n    res = res + \"    files :: \" + filesX.toStr() + \",\\n\";\r\n    res = res + \"    deps  :: [\\n\";\r\n    for i in 0 to (deps.size() - 1) do;\r\n        res = res + \"        \\\"\" + deps.get(i) + \"\\\"\";\r\n        if (i < (deps.size() - 1)) then; res = res + \",\\n\"; end;\r\n    end;\r\n    res = res + \"\\n    ]\\n\/\\n\";\r\n    return res;\r\n};\r\n\r\n--- ================================================================\r\n---   Project Operations\r\n--- ================================================================\r\n\r\nfunc paxNew(array:s: args) {\r\n    if (args.size() < 4) then;\r\n        >! \"Usage: xcx pax new <project_name>\";\r\n        return;\r\n    end;\r\n    s: name = args.get(3);\r\n    >! \"Scaffolding project: \" + name;\r\n    \r\n    store.mkdir(name + \"\/src\");\r\n    \r\n    json: cfg <<< {\"name\": \"\", \"version\": \"0.1.0\", \"author\": \"dev\", \"description\": \"PAX project\", \"deps\": [], \"tags\": [], \"files\": []} >>>;\r\n    cfg.set(\"name\", name);\r\n    \r\n    store.write(name + \"\/project.pax\", toPax(cfg));\r\n    store.write(name + \"\/src\/main.xcx\", \"func main() {\\n    >! \\\"Hello from \" + name + \"!\\\";\\n};\\nmain();\\n\");\r\n    store.write(name + \"\/README.md\", \"# \" + name + \"\\nProfessional XCX project.\");\r\n    \r\n    >! \"Success! Project created in .\/\" + name;\r\n};\r\n\r\nfunc paxRun(array:s: args) {\r\n    s: target = \"src\/main.xcx\";\r\n    if (store.exists(\"project.pax\")) then;\r\n        json: cfg = parsePax(store.read(\"project.pax\"));\r\n        if (cfg.exists(\"main\")) then;\r\n            s: customMain; cfg.bind(\"main\", customMain);\r\n            if (customMain != \"\" AND store.exists(customMain)) then; target = customMain; end;\r\n        end;\r\n    end;\r\n\r\n    if (args.size() > 3) then;\r\n        target = args.get(3);\r\n        if (store.exists(target + \"\/src\/main.xcx\")) then; target = target + \"\/src\/main.xcx\"; end;\r\n    end;\r\n\r\n    if (store.exists(target)) then;\r\n        >! \"Booting: \" + target;\r\n        if (NOT .terminal !run target) then;\r\n            >! \"Runtime Error: Execution aborted.\";\r\n        end;\r\n    else;\r\n        >! \"Error: Target \" + target + \" not found.\";\r\n    end;\r\n};\r\n\r\n--- ================================================================\r\n---   Dependency Management\r\n--- ================================================================\r\n\r\nfunc normalizeUrl(s: input -> s) {\r\n    if (input.startsWith(\"http\")) then; return input; end;\r\n    s: name = input;\r\n    s: ver  = \"latest\";\r\n    if (input HAS \"@\") then;\r\n        array:s: pts = input.split(\"@\");\r\n        name = pts.get(0).trim();\r\n        ver  = pts.get(1).trim();\r\n    end;\r\n    return getRegistryUrl() + \"api?route=api\/packages\/\" + name + \"\/download&v=\" + ver + \"&format=json\";\r\n};\r\n\r\nfunc getPackageName(s: input -> s) {\r\n    s: name = input;\r\n    if (input HAS \"@\") then; name = input.split(\"@\").get(0).trim(); end;\r\n    if (name HAS \"\/\") then;\r\n        array:s: pts = name.split(\"\/\");\r\n        name = pts.get(pts.size() - 1);\r\n    end;\r\n    return name;\r\n};\r\n\r\nfunc updateLockfile(s: name, s: ver, s: path) {\r\n    json: lock <<< {} >>>;\r\n    if (store.exists(\"pax.lock\")) then; lock = json.parse(store.read(\"pax.lock\")); end;\r\n    \r\n    json: entry <<< {\"version\": \"\", \"file\": \"\"} >>>;\r\n    entry.set(\"version\", ver);\r\n    entry.set(\"file\", path);\r\n    lock.set(name, entry);\r\n    \r\n    store.write(\"pax.lock\", lock.toStr());\r\n};\r\n\r\nfunc fetchPackage(s: input, b: isFull) {\r\n    s: url = normalizeUrl(input);\r\n    s: pkg = getPackageName(input);\r\n    s: token = getToken();\r\n    \r\n    map: headers {\r\n        schema = [s <-> s]\r\n        data = [EMPTY]\r\n    };\r\n    if (token != \"\") then; headers.insert(\"X-PAX-TOKEN\", token); end;\r\n\r\n    net.request {\r\n        method = \"GET\",\r\n        url = url,\r\n        headers = headers,\r\n        timeout = 15000,\r\n    } as resp;\r\n\r\n    if (resp.ok) then;\r\n        s: body = s(resp.body);\r\n        if (body.startsWith(\"{\")) then;\r\n            json: m = json.parse(body);\r\n            json: fl;\r\n\r\n            if (m.exists(\"files_list\")) then;\r\n                json: inner = m.get(\"files_list\");\r\n                if (inner.exists(\"files_list\")) then;\r\n                    fl = inner.get(\"files_list\");\r\n                else;\r\n                    fl = inner;\r\n                end;\r\n            end;\r\n\r\n            if (fl.size() == 0) then;\r\n                >! \"  Failed: \" + pkg + \" has no file manifest.\";\r\n            else;\r\n                s: lp = \"lib\/\" + pkg + \"\/\";\r\n                store.mkdir(lp);\r\n\r\n                --- For install mode: read the files list from the package's own project.pax\r\n                array:s: allowedFiles;\r\n                if (NOT isFull) then;\r\n                    i: pi = 0;\r\n                    i: psz = fl.size();\r\n                    while (pi < psz) do;\r\n                        json: pObj = fl.get(pi);\r\n                        s: pp; pObj.bind(\"path\", pp);\r\n                        s: ppNorm = pp.lower().replace(\"\\\\\", \"\/\");\r\n                        if (ppNorm == \"project.pax\" OR ppNorm == \".\/project.pax\") then;\r\n                            s: pc; pObj.bind(\"content\", pc);\r\n                            json: pkgCfg = parsePax(pc);\r\n                            allowedFiles = pkgCfg.get(\"files\");\r\n                        end;\r\n                        pi = pi + 1;\r\n                    end;\r\n                end;\r\n\r\n                i: k = 0;\r\n                i: sz = fl.size();\r\n                while (k < sz) do;\r\n                    json: fObj = fl.get(k);\r\n                    s: p; fObj.bind(\"path\", p);\r\n                    s: c; fObj.bind(\"content\", c);\r\n\r\n                    --- Install mode: filter by declared files list\r\n                    if (NOT isFull) then;\r\n                        b: allowed = false;\r\n                        s: pNorm = p.replace(\"\\\\\", \"\/\");\r\n\r\n                        if (allowedFiles.size() == 0) then;\r\n                            --- Fallback: skip known non-code files when package has no files field\r\n                            s: pl = pNorm.lower();\r\n                            b: isJunk = false;\r\n                            if (pl == \"readme.md\" OR pl == \"license\" OR pl == \"license.txt\") then; isJunk = true; end;\r\n                            if (pl.startsWith(\"tests\/\") OR pl.startsWith(\"docs\/\")) then; isJunk = true; end;\r\n                            if (pl.startsWith(\".git\") OR pl.startsWith(\".github\")) then; isJunk = true; end;\r\n                            if (pl == \"project.pax\" OR pl == \".\/project.pax\") then; isJunk = true; end;\r\n                            if (NOT isJunk) then; allowed = true; end;\r\n                        else;\r\n                            --- Match path against each entry in files list\r\n                            i: ai = 0;\r\n                            i: asz = allowedFiles.size();\r\n                            while (ai < asz) do;\r\n                                s: af = allowedFiles.get(ai).replace(\"\\\\\", \"\/\");\r\n                                if (pNorm == af OR pNorm.endsWith(\"\/\" + af) OR af.endsWith(\"\/\" + pNorm)) then;\r\n                                    allowed = true;\r\n                                end;\r\n                                ai = ai + 1;\r\n                            end;\r\n                        end;\r\n\r\n                        if (NOT allowed) then; k = k + 1; continue; end;\r\n                    end;\r\n\r\n                    --- Directory handling (normalise to forward slashes)\r\n                    s: pSlash = p.replace(\"\\\\\", \"\/\");\r\n                    b: has_dir = pSlash HAS \"\/\";\r\n                    if (has_dir) then;\r\n                        array:s: parts = pSlash.split(\"\/\");\r\n                        s: curPath = lp;\r\n                        for i in 0 to (parts.size() - 2) do;\r\n                            curPath = curPath + parts.get(i) + \"\/\";\r\n                            if (NOT store.exists(curPath)) then; store.mkdir(curPath); end;\r\n                        end;\r\n                    end;\r\n\r\n                    store.write(lp + pSlash, c);\r\n                    k = k + 1;\r\n                end;\r\n\r\n                updateLockfile(pkg, input, lp);\r\n\r\n                s: mode = \"Installed\";\r\n                if (isFull) then; mode = \"Cloned\"; end;\r\n                >! \"  OK: \" + mode + \" \" + pkg + \" in \" + lp;\r\n            end;\r\n        else;\r\n            s: fn = \"pkg_\" + pkg + \"_\" + crypto.token(4) + \".xcx\";\r\n            store.write(\"lib\/\" + fn, resp.body);\r\n            updateLockfile(pkg, input, \"lib\/\" + fn);\r\n            >! \"  OK: Saved as lib\/\" + fn;\r\n        end;\r\n    else;\r\n        >! \"  Failed: Status \" + s(resp.status);\r\n    end;\r\n};\r\n\r\nfunc paxInstall(array:s: args) {\r\n    if (NOT store.exists(\"project.pax\")) then;\r\n        >! \"Error: project.pax not found.\";\r\n        return;\r\n    end;\r\n    \r\n    json: cfg = parsePax(store.read(\"project.pax\"));\r\n    array:s: deps = cfg.get(\"deps\");\r\n    >! \"Resolving \" + s(deps.size()) + \" dependencies...\";\r\n    \r\n    array:s: fetched;\r\n    for d in deps do;\r\n        s: name = getPackageName(d);\r\n        if (NOT fetched.contains(name)) then;\r\n            >! \"Fetching \" + name + \"...\";\r\n            fetchPackage(d, false);\r\n            fetched.push(name);\r\n        end;\r\n    end;\r\n    >! \"Installation complete.\";\r\n};\r\n\r\nfunc paxClone(array:s: args) {\r\n    if (args.size() < 4) then;\r\n        >! \"Usage: xcx pax clone <package>\";\r\n        return;\r\n    end;\r\n    s: target = args.get(3);\r\n    >! \"Cloning package \" + target + \"...\";\r\n    fetchPackage(target, true);\r\n};\r\n\r\nfunc paxAdd(array:s: args) {\r\n    if (args.size() < 4 OR NOT store.exists(\"project.pax\")) then;\r\n        >! \"Usage: xcx pax add <package>\";\r\n        return;\r\n    end;\r\n    \r\n    s: name = args.get(3);\r\n    json: cfg = parsePax(store.read(\"project.pax\"));\r\n    cfg.get(\"deps\").push(name);\r\n    store.write(\"project.pax\", toPax(cfg));\r\n    >! \"Added \" + name + \" to project.pax. Running install...\";\r\n    paxInstall(args);\r\n};\r\n\r\nfunc paxRemove(array:s: args) {\r\n    if (args.size() < 4 OR NOT store.exists(\"project.pax\")) then;\r\n        >! \"Usage: xcx pax remove <n>\";\r\n        return;\r\n    end;\r\n    s: target = args.get(3);\r\n    json: cfg = parsePax(store.read(\"project.pax\"));\r\n    array:s: deps = cfg.get(\"deps\");\r\n    i: idx = -1;\r\n    for i in 0 to (deps.size() - 1) do;\r\n        if (deps.get(i) HAS target) then; idx = i; end;\r\n    end;\r\n    \r\n    if (idx >= 0) then;\r\n        deps.delete(idx);\r\n        store.write(\"project.pax\", toPax(cfg));\r\n        >! \"Removed \" + target + \". Note: lib\/ folders and pax.lock must be cleaned manually or via install.\";\r\n    end;\r\n};\r\n\r\n--- ================================================================\r\n---   Registry & Publishing\r\n--- ================================================================\r\n\r\nfunc paxSearch(array:s: args) {\r\n    if (args.size() < 4) then; return; end;\r\n    s: q = args.get(3);\r\n    >! \"Searching Registry: \" + q + \"...\";\r\n    net.request {\r\n        method = \"GET\", \r\n        url = getRegistryUrl() + \"api?route=api\/search&q=\" + q,\r\n        timeout = 10000,\r\n    } as resp;\r\n    if (resp.ok) then;\r\n        s: body = s(resp.body);\r\n        if (body.startsWith(\"[\")) then;\r\n            i: sz = resp.body.size();\r\n            if (sz == 0) then; >! \"No matches.\"; return; end;\r\n            i: ri = 0;\r\n            while (ri < sz) do;\r\n                json: res = resp.body.get(ri);\r\n                s: n; res.bind(\"name\", n);\r\n                s: v; res.bind(\"version\", v);\r\n                s: a; res.bind(\"author_name\", a);\r\n                >! \"- \" + n + \" (v\" + v + \") by \" + a;\r\n                ri = ri + 1;\r\n            end;\r\n        else;\r\n            >! \"Error: Registry returned unexpected search response.\";\r\n        end;\r\n    end;\r\n};\r\n\r\nfunc scanRecursive(s: dir, array:json: list) {\r\n    array:s: items = store.list(dir);\r\n    for item in items do;\r\n        s: p = dir + item;\r\n        if (store.isDir(p)) then;\r\n            scanRecursive(p + \"\/\", list);\r\n        else;\r\n            json: f <<< {\"path\": \"\", \"content\": \"\"} >>>;\r\n            f.set(\"path\", p);\r\n            f.set(\"content\", store.read(p));\r\n            list.push(f);\r\n        end;\r\n    end;\r\n};\r\n\r\nfunc paxPublish() {\r\n    if (NOT store.exists(\"project.pax\")) then; return; end;\r\n    s: token = getToken();\r\n    if (token == \"\") then; >! \"Login required.\"; return; end;\r\n    \r\n    json: cfg = parsePax(store.read(\"project.pax\"));\r\n    s: name; cfg.bind(\"name\", name);\r\n    s: ver;  cfg.bind(\"version\", ver);\r\n    \r\n    s: zipFile = name + \"_\" + ver + \".zip\";\r\n    >! \"Compressing project into \" + zipFile + \"...\";\r\n    \r\n    if (NOT store.zip(\".\", zipFile)) then;\r\n        halt.error >! \"Failed to create project archive.\";\r\n        return;\r\n    end;\r\n\r\n    array:json: flLine;\r\n    scanRecursive(\".\/\", flLine);\r\n    array:json: flClean;\r\n    i: fli = 0;\r\n    i: flsz = flLine.size();\r\n    while (fli < flsz) do;\r\n        json: f = flLine.get(fli);\r\n        s: fp; f.bind(\"path\", fp);\r\n        if (NOT fp.endsWith(\".zip\") AND NOT (fp HAS \".pax_token\") AND NOT (fp HAS \".git\")) then;\r\n            flClean.push(f);\r\n        end;\r\n        fli = fli + 1;\r\n    end;\r\n    \r\n    s: readme = \"\";\r\n    i: q = 0;\r\n    i: qsz = flClean.size();\r\n    while (q < qsz) do;\r\n        json: f = flClean.get(q);\r\n        s: p; f.bind(\"path\", p);\r\n        if (p.lower() == \"readme.md\" OR p.lower() == \".\/readme.md\") then;\r\n            f.bind(\"content\", readme);\r\n        end;\r\n        q = q + 1;\r\n    end;\r\n    \r\n    json: payload <<< {\"name\": \"\", \"version\": \"\", \"description\": \"\", \"readme_content\": \"\", \"zip_content\": \"\", \"contents_json\": \"\"} >>>;\r\n    payload.set(\"name\", name);\r\n    payload.set(\"version\", ver);\r\n    payload.set(\"description\", cfg.get(\"description\"));\r\n    payload.set(\"readme_content\", readme);\r\n    payload.set(\"zip_content\", crypto.hash(store.read(zipFile), \"base64_encode\"));\r\n    payload.set(\"contents_json\", flClean.toStr());\r\n    \r\n    >! \"Pushing package archive to Registry...\";\r\n    map: pubHeaders {\r\n        schema = [s <-> s]\r\n        data = [\"X-PAX-TOKEN\" :: token, \"Content-Type\" :: \"application\/json\"]\r\n    };\r\n    net.request {\r\n        method = \"POST\",\r\n        url = getRegistryUrl() + \"api?route=api\/publish\",\r\n        headers = pubHeaders,\r\n        body = payload,\r\n        timeout = 30000,\r\n    } as resp;\r\n    \r\n    --- Cleanup\r\n    store.delete(zipFile);\r\n    \r\n    if (resp.ok) then; \r\n        >! \"Success! Package published.\"; \r\n    else; \r\n        s: errMsg = \"\";\r\n        if (resp.exists(\"text\")) then;\r\n            s: raw = s(resp.get(\"text\")).trim();\r\n            if (raw.startsWith(\"{\")) then;\r\n                json: parsed = json.parse(raw);\r\n                if (parsed.exists(\"message\")) then;\r\n                    parsed.bind(\"message\", errMsg);\r\n                elif (parsed.exists(\"error\")) then;\r\n                    parsed.bind(\"error\", errMsg);\r\n                end;\r\n            else;\r\n                errMsg = raw;\r\n            end;\r\n        end;\r\n        \r\n        if (errMsg == \"\") then;\r\n            resp.bind(\"error\", errMsg);\r\n        end;\r\n        \r\n        >! \"Publish Failed (HTTP \" + s(resp.get(\"status\")) + \"): \" + errMsg;\r\n    end;\r\n};\r\n\r\n--- ================================================================\r\n---   Upgrade Management\r\n--- ================================================================\r\n\r\nfunc detectOS(-> s) {\r\n    array:s: args = env.args();\r\n    if (args.size() > 0) then;\r\n        s: exe = args.get(0);\r\n        if (exe HAS \"\\\\\" OR exe HAS \".exe\") then;\r\n            return \"windows\";\r\n        end;\r\n    end;\r\n    s: home = env.get(\"HOME\");\r\n    if (home.startsWith(\"\/Users\") OR home.startsWith(\"\/var\/root\")) then;\r\n        return \"macos\";\r\n    end;\r\n    return \"linux\";\r\n};\r\n\r\nfunc getInstallDir(-> s) {\r\n    array:s: args = env.args();\r\n    if (args.size() == 0) then; return \"\"; end;\r\n    s: exe = args.get(0).replace(\"\\\\\", \"\/\");\r\n    array:s: parts = exe.split(\"\/\");\r\n    s: result = \"\";\r\n    i: stop = parts.size() - 2;\r\n    if (stop <= 0) then; return \"\"; end;\r\n    for i in 0 to (stop - 1) do;\r\n        if (result != \"\") then; result = result + \"\/\"; end;\r\n        result = result + parts.get(i);\r\n    end;\r\n    return result;\r\n};\r\n\r\nfunc getLatestGitHubTag(-> s) {\r\n    map: headers {\r\n        schema = [s <-> s]\r\n        data = [\"User-Agent\" :: \"xcx-pax\"]\r\n    };\r\n    net.request {\r\n        method = \"GET\",\r\n        url = \"https:\/\/api.github.com\/repos\/xcxlang-org\/xcx\/releases\/latest\",\r\n        headers = headers,\r\n        timeout = 10000,\r\n    } as resp;\r\n    if (resp.ok) then;\r\n        s: body = s(resp.body);\r\n        if (body.startsWith(\"{\")) then;\r\n            json: data = resp.body;\r\n            if (data.exists(\"tag_name\")) then;\r\n                s: tag;\r\n                data.bind(\"tag_name\", tag);\r\n                return tag;\r\n            end;\r\n        end;\r\n    end;\r\n    return \"\";\r\n};\r\n\r\nfunc getLocalXcxVersion(-> s) {\r\n    s: installDir = getInstallDir();\r\n    s: vfile = installDir + \"\/lib\/VERSION\";\r\n    if (store.exists(vfile)) then;\r\n        return store.read(vfile).trim();\r\n    end;\r\n    if (store.exists(\"lib\/VERSION\")) then;\r\n        return store.read(\"lib\/VERSION\").trim();\r\n    end;\r\n    return \"unknown\";\r\n};\r\n\r\nfunc upgradeXcxBinary(s: installDir, s: os) {\r\n    s: tag = getLatestGitHubTag();\r\n    if (tag == \"\") then;\r\n        >! \"Error: Could not fetch latest release info from GitHub.\";\r\n        return;\r\n    end;\r\n    s: base = \"https:\/\/github.com\/xcxlang-org\/xcx\/releases\/download\/\" + tag + \"\/\";\r\n    s: downloadName = \"xcx\";\r\n    s: localName = \"xcx\";\r\n    \r\n    if (os == \"windows\") then;\r\n        downloadName = \"xcx.exe\";\r\n        localName = \"xcx.exe\";\r\n    elif (os == \"macos\") then;\r\n        downloadName = \"xcx-macos\";\r\n        localName = \"xcx\";\r\n    elif (os == \"linux\") then;\r\n        downloadName = \"xcx-linux\";\r\n        localName = \"xcx\";\r\n    end;\r\n    \r\n    s: url = base + downloadName;\r\n    >! \"Downloading \" + downloadName + \" version \" + tag + \"...\";\r\n    map: headers {\r\n        schema = [s <-> s]\r\n        data = [\"User-Agent\" :: \"xcx-pax\"]\r\n    };\r\n    net.request {\r\n        method = \"GET\",\r\n        url = url,\r\n        headers = headers,\r\n        timeout = 60000,\r\n    } as resp;\r\n    if (resp.ok) then;\r\n        s: destBinDir = installDir + \"\/bin\";\r\n        if (NOT store.exists(destBinDir)) then;\r\n            store.mkdir(destBinDir);\r\n        end;\r\n        s: dest = destBinDir + \"\/\" + localName;\r\n        s: body = s(resp.body);\r\n        if (store.write(dest, body)) then;\r\n            >! \"OK: xcx binary updated at \" + dest;\r\n            if (os != \"windows\") then;\r\n                .terminal !run \"chmod +x \" + dest;\r\n            end;\r\n        else;\r\n            >! \"Error: Permission denied. Could not write to \" + dest;\r\n            if (os == \"windows\") then;\r\n                >! \"Please run PowerShell\/CMD as Administrator and try again.\";\r\n            else;\r\n                >! \"Please check your write permissions or run with sudo and try again.\";\r\n            end;\r\n        end;\r\n    else;\r\n        >! \"Error: Download failed (HTTP \" + s(resp.status) + \").\";\r\n    end;\r\n};\r\n\r\nfunc fetchToolPackage(s: pkgName, s: destLibDir) {\r\n    s: os = detectOS();\r\n    s: url = getRegistryUrl() + \"api?route=api\/packages\/\" + pkgName + \"\/download&v=latest&format=json\";\r\n    s: token = getToken();\r\n    map: headers {\r\n        schema = [s <-> s]\r\n        data = [EMPTY]\r\n    };\r\n    if (token != \"\") then; headers.insert(\"X-PAX-TOKEN\", token); end;\r\n    net.request {\r\n        method = \"GET\",\r\n        url = url,\r\n        headers = headers,\r\n        timeout = 15000,\r\n    } as resp;\r\n    if (resp.ok) then;\r\n        s: body = s(resp.body);\r\n        if (body.startsWith(\"{\")) then;\r\n            json: m = json.parse(body);\r\n            json: fl;\r\n            if (m.exists(\"files_list\")) then;\r\n                json: inner = m.get(\"files_list\");\r\n                if (inner.exists(\"files_list\")) then;\r\n                    fl = inner.get(\"files_list\");\r\n                else;\r\n                    fl = inner;\r\n                end;\r\n            end;\r\n            if (fl.size() == 0) then;\r\n                >! \"  Failed: \" + pkgName + \" has no file manifest.\";\r\n            else;\r\n                s: lp = destLibDir + \"\/\";\r\n                i: k = 0;\r\n                i: sz = fl.size();\r\n                while (k < sz) do;\r\n                    json: fObj = fl.get(k);\r\n                    s: p; fObj.bind(\"path\", p);\r\n                    s: c; fObj.bind(\"content\", c);\r\n                    s: pSlash = p.replace(\"\\\\\", \"\/\");\r\n                    b: has_dir = pSlash HAS \"\/\";\r\n                    if (has_dir) then;\r\n                        array:s: parts = pSlash.split(\"\/\");\r\n                        s: curPath = lp;\r\n                        for i in 0 to (parts.size() - 2) do;\r\n                            curPath = curPath + parts.get(i) + \"\/\";\r\n                            if (NOT store.exists(curPath)) then; store.mkdir(curPath); end;\r\n                        end;\r\n                    end;\r\n                    if (NOT store.write(lp + pSlash, c)) then;\r\n                        >! \"Error: Permission denied writing package file: \" + lp + pSlash;\r\n                        if (os == \"windows\") then;\r\n                            >! \"Please run PowerShell\/CMD as Administrator and try again.\";\r\n                        else;\r\n                            >! \"Please check your write permissions or run with sudo and try again.\";\r\n                        end;\r\n                        return;\r\n                    end;\r\n                    if (pSlash.endsWith(\"\/math.xcx\") OR pSlash == \"math.xcx\") then;\r\n                        if (NOT store.write(lp + \"math.xcx\", c)) then;\r\n                            >! \"Error: Permission denied writing math.xcx file.\";\r\n                            return;\r\n                        end;\r\n                    end;\r\n                    k = k + 1;\r\n                end;\r\n                >! \"  OK: Updated \" + pkgName;\r\n            end;\r\n        else;\r\n            >! \"  Failed: Registry returned non-JSON structure for \" + pkgName;\r\n        end;\r\n    else;\r\n        >! \"  Failed: Status \" + s(resp.status) + \" for \" + pkgName;\r\n    end;\r\n};\r\n\r\nfunc upgradeTools(s: installDir) {\r\n    s: libDir = installDir + \"\/lib\";\r\n    >! \"Upgrading tools to latest from PAX registry...\";\r\n    fetchToolPackage(\"pax\", libDir);\r\n    fetchToolPackage(\"mathlib\", libDir);\r\n    fetchToolPackage(\"doc\", libDir);\r\n};\r\n\r\nfunc paxUpgrade(array:s: args) {\r\n    if (args.size() < 4) then;\r\n        >! \"Error: specify what to upgrade: xcx, tools, all\";\r\n        return;\r\n    end;\r\n    s: target = args.get(3);\r\n    b: checkOnly = false;\r\n    if (target == \"--check\") then;\r\n        target = \"all\";\r\n        checkOnly = true;\r\n    end;\r\n    if (args.size() >= 5) then;\r\n        if (args.get(4) == \"--check\") then;\r\n            checkOnly = true;\r\n        end;\r\n    end;\r\n    if (target != \"xcx\" AND target != \"tools\" AND target != \"all\") then;\r\n        >! \"Error: unknown target '\" + target + \"'. Use: xcx, tools, all\";\r\n        return;\r\n    end;\r\n    s: os = detectOS();\r\n    s: installDir = getInstallDir();\r\n    if (installDir == \"\") then;\r\n        >! \"Error: Could not determine install directory.\";\r\n        return;\r\n    end;\r\n    if (checkOnly) then;\r\n        s: local = getLocalXcxVersion();\r\n        s: remote = getLatestGitHubTag();\r\n        if (remote == \"\") then;\r\n            >! \"Error: Could not retrieve remote version from GitHub.\";\r\n            return;\r\n        end;\r\n        >! \"Local version:  \" + local;\r\n        >! \"Latest release: \" + remote;\r\n        if (local == remote OR \"v\" + local == remote) then;\r\n            >! \"Already up to date.\";\r\n        else;\r\n            >! \"Update available: \" + remote;\r\n        end;\r\n        return;\r\n    end;\r\n    if (target == \"xcx\" OR target == \"all\") then;\r\n        upgradeXcxBinary(installDir, os);\r\n    end;\r\n    if (target == \"tools\" OR target == \"all\") then;\r\n        upgradeTools(installDir);\r\n    end;\r\n    >! \"Upgrade complete.\";\r\n};\r\n\r\nmain();\r\n"}]}}