# InterSkip

Python resolver and an additive Tampermonkey-compatible userscript for the
Intercelestial handoff. pahe.plus is the preferred and most common destination;
the resolver also recognizes configured aliases and can return an unknown
external destination after the final gate action.

## Python

```powershell
python -m pip install -r requirements.txt
python .\interskip.py "https://intercelestial.com/?ht=..." --ignore-captchas
```

The Python resolver uses installed Microsoft Edge through Playwright. It runs
headless by default; pass `--show-browser` to display Edge. `--max-clicks 12`
limits the number of visible gate controls it may click. It supports the usual
Click To Verify -> Continue -> Generate Link -> Continue -> Get Link flow,
including controls rendered as anchors and common label variations.

Use `--intercelestial-domain`, `--pahe-domain`, and `--ouo-domain` if the stage
hostnames change. Repeat a flag or provide comma-separated hosts to configure
aliases, for example `--intercelestial-domain intercelestial.com,intercelestial.net`.
Use `--ignore-captchas` to continue scanning for gate
controls when CAPTCHA indicators are present; it does not interact with or
solve a challenge.

Gate progress, browser cookies, and local storage are saved under
`~/.interskip` by default. The checkpoint records the input URL, last same-site
URL, and next flow step so a new run can resume. Use `--state-dir PATH` to
change the location, or `--no-persist-state` to opt out.

The Python resolver advances timers with Playwright's page clock. It closes
unrelated popup tabs and blocks unrelated top-level navigations before they
replace the gate page. Same-site Intercelestial paths remain allowed. It stops
at a configured pahe.plus or OuO host when one is reached. After the final Get
Link action, it also returns an unconfigured external destination. It does not
bypass the secondary pahe.plus -> ouo.io stage or solve CAPTCHAs.

## Experimental HTTP-only probe

`interskip_http.py` is a separate, standard-library-only experimental resolver;
it does not change the Playwright resolver. By default it follows the initial
hidden `hq` handoff and reports gate fields and any literal configured pahe.plus
URLs in the response. Pass `--advance` to replay the observed gate POSTs and
page-view beacons without opening a browser. It prefers configured pahe.plus
and OuO hosts, then falls back to an external URL found in a compact handoff
response:

```powershell
python .\interskip_http.py "https://intercelestial.com/?ht=..." --advance
```

The page supplies a changing `LLPayload` and records trusted-click and timing
fields. The prototype derives those fields from the page's current gate shape,
then follows hidden `hq` handoffs and checks for the destination. Its HTTP-only
behavior is still experimental; use the Playwright resolver if it does not
reach the destination. The supplied sample currently resolves through two
gate submissions to `https://pahe.plus/g2ZBPZ` without launching a browser; an
unknown host in a compact final handoff is returned as the generic destination.
The alternate test URL you supplied resolved to `https://linegee.net/LZwSc`;
that host and path are extracted dynamically and are not hardcoded.
Both inputs and expected results are recorded in [test_urls.json](test_urls.json)
for future regression checks.

## Userscript

Install `interskip.user.js` in Tampermonkey on Chromium or Firefox. The
`DOMAINS` object near the top configures stage hosts as arrays, so each service
can have multiple accepted hostnames, for example
`intercelestial: ['intercelestial.com', 'intercelestial.net']`. The metadata matches
HTTP(S) pages broadly so updated hostnames still work; the script exits
immediately unless the current host matches a configured domain. It does not
depend on page IDs or site-specific classes.

On both Intercelestial and pahe.plus, the userscript can accelerate long
JavaScript timers, blocks unrelated popups, guards external anchors/forms, and
uses the Navigation API to cancel unrelated top-level navigations when the
browser marks them cancellable. Automatic gate-button progression runs on
Intercelestial only. The final Get Link step may navigate to an unknown external
host; configured pahe.plus domains remain recognized as the common destination.
Timer acceleration is controlled by the global `ACCELERATE_TIMERS` boolean.
Optional per-host overrides in `TIMER_ACCELERATION_BY_DOMAIN` take precedence
over the global setting and match that hostname plus its subdomains. For
example, set `ACCELERATE_TIMERS = true` and
`TIMER_ACCELERATION_BY_DOMAIN = { 'pahe.plus': false }` to keep Intercelestial's
timer acceleration while leaving pahe.plus timers native. Set the global value
to `false` to disable it everywhere; a per-host `true` can enable it for one
host again. The HTTP-only userscript does not accelerate page timers.
Some link variants require a browser-trusted submit click. Set
`REQUIRE_TRUSTED_FORM_CLICK` to `true` to pause on each final form button and
let the user click it. Alternatively, set `PATCH_TRUSTED_CLICK_CHECKS` to
`true` to opt in to setting the gate's `ll_ct` field to `1` while retaining
automatic clicks. Timeline fields are normalized on gate submissions to match
the accelerated timer sequence. Both settings are off by default.
Set `PERSIST_GATE_STATE` to `false` to disable its saved
flow-step/page checkpoint, which otherwise expires after two hours.

The live userscript baseline was tested in Playwright-controlled Edge/Chromium
and Firefox browser sessions with the script injected at document start. Both
runs reached pahe.plus on the common link variant. The alternate linegee.net
link also reached its destination in Edge and Firefox with
`PATCH_TRUSTED_CLICK_CHECKS` enabled; synthetic clicks without that opt-in did
not satisfy this variant.
The userscript retains permission for an unknown final navigation across
same-site article navigations. On pahe.plus, three visible Continue clicks followed by a
45-second watch stayed on the same page in both engines. The timer check
reduced a 2-second timeout to about 100 ms; popup, external-link, and form
guards passed on both sites. The tests did not install the Tampermonkey
extension into the user's existing browser profile.

uBlock Origin's timer adjustment model is described in its
[resource library](https://github.com/gorhill/uBlock/wiki/Resources-Library#adjust-settimeoutjs).
The Navigation API's ability to cancel navigations depends on browser support
and the event's cancellable state, as described in
[MDN's NavigateEvent reference](https://developer.mozilla.org/en-US/docs/Web/API/NavigateEvent).

## Destination domain allowlist

All three resolvers support an opt-in final destination allowlist. Python accepts
repeatable `--destination-domain HOST[,HOST...]` options in both
`interskip.py` and `interskip_http.py`; `interskip.user.js` has a
`DESTINATION_DOMAINS` array near the top. The HTTP userscript accepts the
`destinationDomains` option when calling its API. Empty/unset configuration
preserves unrestricted generic destination detection. Matching is case-
insensitive, ignores a leading `www.`, and accepts either an exact hostname or
one of its subdomains. A configured list that excludes the resolved host causes
Python to return exit status 3; the HTTP extractor returns a result marked
`rejected`; the click userscript blocks the disallowed external navigation.

## HTTP userscript and reusable extractor

`interskip_http.user.js` is the HTTP-only Tampermonkey adapter. It exposes
`globalThis.InterSkipHttp` before checking the current site's host, so a page
can load the file as a regular script and call the reusable API:

```js
const result = await InterSkipHttp.resolve(startUrl, requestFunction, {
  destinationDomains: ['pahe.plus', 'linegee.net'],
});
```

`requestFunction` accepts `{url, method, body, headers, referer}` and returns
`{url, html}`. This keeps network policy separate: browsers need a CORS-enabled
endpoint or proxy for cross-origin requests, while a serverless backend can
provide its own requester. `extractDestination`, `extractHqForm`, and
`extractGatePayload` are also available for callers that already have HTML.
In a browser, `InterSkipHttp.createBrowserRequester()` uses same-origin `fetch`
and the current session, then Tampermonkey's `GM_xmlhttpRequest` for
cross-origin requests; outside Tampermonkey it supports same-origin requests
and reports when cross-origin access needs a custom requester.
The userscript wrapper uses Tampermonkey's cross-origin request API and starts
only on configured Intercelestial hosts. It uses the page's normal `fetch`
session for same-origin requests (including browser cookies), and
`GM_xmlhttpRequest` for cross-origin requests. This can reuse a browser session
that has already passed the site's normal wait/challenge page; it does not
solve or bypass that challenge. The standalone Python client has no browser
cookies or JavaScript challenge session, so its results can vary with the
site's automated-traffic checks; retry later or use the browser-based resolver
if the server returns a challenge page. Both HTTP clients make up to two normal
retries after a “Please wait” response, with a 4-second initial delay and
exponential backoff. Retries replay the original method and body (including a
gate POST) so they do not replace a stateful submission with a GET. Persistent
challenges are reported without attempting to solve them. Retry count and delay are configurable in the Python CLI and via
`challengeRetries` / `challengeRetryDelayMs` in `InterSkipHttp.resolve`.

## Static resolver website

The root files (`index.html`, `app.js`, `styles.css`, and `services.js`) form a
dependency-free static website. The shared resolver core at
`api/resolve-core.mjs` imports the same `InterSkipHttp.resolve` implementation
from the userscript. It keeps a cookie jar for one resolution, fetches only
configured Intercelestial, pahe.plus, and OuO hosts, and returns an external
redirect as a destination without requesting that host. Thin platform adapters
are provided for Vercel (`api/resolve.js`), Netlify
(`netlify/functions/resolve.mjs`), and Cloudflare Pages
(`functions/api/resolve.js`).

Run it locally with Node 18.17 or newer:

```powershell
npm run dev
```

Then open `http://127.0.0.1:4173`. Vercel, Netlify, and Cloudflare Pages can
each serve the static page and their `/api/resolve` function from one origin.
`npm run build:static` copies only frontend files to `dist/`; set
`INTERSKIP_SAME_ORIGIN_API=true` when that platform also deploys its function.
For GitHub Pages, which is static-only, set `INTERSKIP_API_BASE` to an external
API origin instead. The generated frontend uses relative assets, so GitHub
project-site subpaths work. The root `site-config.js` remains empty for Vercel
and local development, so those same-origin setups are unchanged.

Add future services in `services.js` and provide a resolver adapter for its
`resolver` identifier in `api/resolve-core.mjs`; the input validation, result panel,
and layout already use the registry rather than hardcoding a service picker.
Intercelestial is the only registered service today. To change its accepted
domains, update its `domains` array; pahe.plus and OuO aliases can be set through
the `PAHE_DOMAINS` and `OUO_DOMAINS` environment variables on the API host.

### GitHub Pages setup

1. Deploy this repository to Netlify once. Its UI and `/api/resolve` function
   deploy together; copy the resulting HTTPS origin, such as
   `https://your-project.netlify.app`.
2. In the GitHub repository, open **Settings → Secrets and variables → Actions
   → Variables** and add `INTERSKIP_API_BASE` with that origin. It is a public
   URL, so add it as a variable, not a secret.
3. Open **Settings → Pages** and set the build and deployment source to
   **GitHub Actions**.
4. Push to `main`. The workflow builds a clean `dist/` containing only the
   static site, then deploys it. The expected project-site URL is
   `https://mostafawalid10.github.io/InterSkip/` for this repository.

The Pages workflow is in `.github/workflows/pages.yml`. It uses relative asset
paths and generates a GitHub-specific API config in `dist/`, leaving Vercel's
same-origin configuration unchanged. GitHub Pages serves the UI while Netlify
provides the API endpoint.

### Netlify setup

1. In Netlify, choose **Add new site → Import an existing project** and connect
   this repository.
2. Netlify reads `netlify.toml`: build command `node scripts/build-static.js`,
   publish directory `dist`, and the resolver function. No external API origin
   is needed.
3. Deploy. Netlify serves `/api/resolve` from its function and the static UI
   from the same site origin. Optional `PAHE_DOMAINS` and `OUO_DOMAINS` runtime
   variables configure stage aliases. See Netlify's docs for
   [function configuration](https://docs.netlify.com/build/functions/configuration/)
   and [build configuration](https://docs.netlify.com/build/configure-builds/file-based-configuration/).

### Cloudflare Pages setup

Use one of these deployment paths:

- **Cloudflare Pages Git integration:** create a Pages project connected to
  this repository, set build command `node scripts/build-static.js` and output
  directory `dist`, then let Pages deploy the build. Do not set a custom deploy
  command.
- **A CI or build system that runs Wrangler:** after the build, use
  `npx wrangler pages deploy dist --project-name interskip` (replace `interskip`
  with your Pages project name). Do not use `npx wrangler deploy`; that deploys
  a Workers script/assets project, not this Pages project.

For either path, set build variable `INTERSKIP_SAME_ORIGIN_API=true`. The Pages
Function is in `functions/api/resolve.js`; `wrangler.jsonc` sets the Pages
output directory and compatibility date. Optional `PAHE_DOMAINS` and
`OUO_DOMAINS` aliases can be added under `vars` in `wrangler.jsonc`. The UI and
API function then share one origin. See Cloudflare's [Pages Functions
guide](https://developers.cloudflare.com/pages/functions/get-started/) and
[Wrangler Pages configuration reference](https://developers.cloudflare.com/pages/functions/wrangler-configuration/).
