From eec0219641d4e6010db8719545696496c0c29ea5 Mon Sep 17 00:00:00 2001 From: Phillip Yan Date: Sat, 7 Feb 2026 17:04:00 -0500 Subject: [PATCH] Working manual login flow --- .gitignore | 4 + CLAUDE.md | 70 ++++++++++++--- README.md | 99 +++++++++++++++++++-- docs/auth-setup.md | 159 ++++++++++++++++++++++++++++++++++ docs/project-tracker.md | 143 +++++++++++++++++++++++++----- package.json | 4 +- scripts/capture-auth-state.ts | 63 ++++++++++++++ scripts/demo-auth-state.ts | 51 +++++++++++ 8 files changed, 550 insertions(+), 43 deletions(-) create mode 100644 docs/auth-setup.md create mode 100644 scripts/capture-auth-state.ts create mode 100644 scripts/demo-auth-state.ts diff --git a/.gitignore b/.gitignore index 6b41865..ee2cae7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,10 @@ *.pem secrets/ +# Auth state (captured from manual login — never commit) +auth/ +auth_profile/ + # Dependencies node_modules/ diff --git a/CLAUDE.md b/CLAUDE.md index e207c62..576678b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,9 @@ This file defines the **permanent operating rules** for Claude Code in this repo |-------------|---------------------|--------| | Browser automation | **Playwright** | Intercept `/api/simple-chat`, drive UI (action box, advisor box). | | Runtime | **Node.js + TypeScript** | Preferred for Playwright and tooling; use Python only if explicitly required. | -| LLM | TBD (API or local) | Used for reasoning and batch-of-actions generation. | +| LLM | **Anthropic Claude API** (via SDK) | Reasoning and batch-of-actions generation. | +| Validation | **Zod** | Schema validation at all JSON boundaries. | +| TS execution | **tsx** | Fast TypeScript execution (replaces ts-node). | | War Room storage | JSON + Markdown + TXT in `war-room/` | See folder layout below. | --- @@ -40,7 +42,11 @@ This file defines the **permanent operating rules** for Claude Code in this repo │ ├── crisis_handbook.txt # Tactical playbook. │ ├── strategic_ledger.json # Active plans / long-term memory. │ └── current_state.json # Latest game state (written by Spy). -├── src/ # Application code (structure TBD: e.g. spy/, brain/, hand/). +├── src/ # Application code. +│ ├── spy/ # Perception layer (Playwright intercept, state parsing). +│ ├── brain/ # Reasoning layer (context assembly, LLM calls, action generation). +│ ├── hand/ # Execution layer (Playwright UI automation). +│ └── shared/ # Types, Zod schemas, utilities shared across modules. └── scripts/ # One-off or scaffold scripts (e.g. scaffold.sh). ``` @@ -56,7 +62,49 @@ This file defines the **permanent operating rules** for Claude Code in this repo --- -## 5. Workflow + Git Rules +## 5. Pre-flight Rule + +**Before creating any new file or module**, check this file (`CLAUDE.md`) to confirm the correct path, naming convention, and module boundary. If the file doesn't fit the canonical structure, stop and discuss. + +--- + +## 6. Coding Standards + +- **Functional-first:** No classes unless there's a clear reason (e.g. Playwright Page wrapper). Prefer plain functions and modules. +- **Zod at boundaries:** Every JSON file read/write and every external API response gets a Zod schema. Validate, don't assume. +- **Explicit return types** on all exported functions. +- **Barrel exports:** Each module directory (`spy/`, `brain/`, `hand/`, `shared/`) has an `index.ts` re-exporting its public API. +- **No `any`:** Use `unknown` + Zod parsing instead. TypeScript strict mode. +- **Imports:** Prefer relative imports within a module; use `../shared/` for cross-module shared code. + +--- + +## 7. Architecture — Data-Flow Contracts + +The cognitive loop passes data through well-defined boundaries: + +``` +Spy (Playwright intercept) + → writes current_state.json (Zod-validated GameState) + → emits "new-turn" event + +Brain (context assembly) + ← reads current_state.json, constitution.md, crisis_handbook.txt, strategic_ledger.json + → calls Anthropic Claude API with assembled prompt + → receives ActionBatch (Zod-validated) + → updates strategic_ledger.json + +Hand (Playwright UI driver) + ← receives ActionBatch from Brain + → types each action into the action box, submits + → optionally queries advisor, returns advice to Brain +``` + +Each boundary has a Zod schema. Modules communicate through files (War Room) and function calls — no shared mutable state. + +--- + +## 8. Workflow + Git Rules - **Branch first:** Always do work on a feature branch, never directly on `main`. Use branch names like `feature/...`, `fix/...`, `chore/...`. - **Commits:** You may create local commits as checkpoints. Keep them meaningful and scoped. **Never push** to the remote—pushing is always a human action. @@ -67,7 +115,7 @@ This file defines the **permanent operating rules** for Claude Code in this repo --- -## 6. Memory Rules (Dual-Document System) +## 9. Memory Rules (Dual-Document System) | File / Location | Type | Purpose | |----------------------|------------|---------| @@ -79,7 +127,7 @@ Do **not** use `CLAUDE.md` for task lists or progress. Update `docs/project-trac --- -## 7. Session Hygiene +## 10. Session Hygiene - Use **`/clear`** when switching to a new, unrelated task (e.g. finishing UI work and starting database work). - Use **`/compact`** during long debugging/refactor sessions to reduce context bloat; preserve key state, discard raw logs. @@ -87,7 +135,7 @@ Do **not** use `CLAUDE.md` for task lists or progress. Update `docs/project-trac --- -## 8. Editing + Safety +## 11. Editing + Safety - Prefer **Plan Mode** for complex or multi-file changes. Always propose a plan before large refactors. - Do not modify unrelated files. Avoid repo-wide formatting or drive-by edits. @@ -95,20 +143,20 @@ Do **not** use `CLAUDE.md` for task lists or progress. Update `docs/project-trac --- -## 9. Quality Gates +## 12. Quality Gates - Keep `main` in a working, deployable state. - Before merging work into `main`, ensure: tests pass; typecheck/lint passes (if applicable); diff is reviewed and scoped. --- -## 10. Parallel Work (Advanced) +## 13. Parallel Work (Advanced) - If running multiple Claude sessions, isolate work using Git worktrees: one branch + one worktree per task; avoid overlapping edits on the same files. --- -## 11. Behavioral Guardrails (#) +## 14. Behavioral Guardrails (#) These rules are permanent. Follow them unless the user explicitly overrides in the current conversation. @@ -124,13 +172,13 @@ These rules are permanent. Follow them unless the user explicitly overrides in t --- -## 12. Default Operating Principle +## 15. Default Operating Principle Claude should behave like a careful engineering teammate: small, reversible steps; explicit plans for complex work; branch isolation; human-controlled pushes; repo stays clean and readable. --- -## 13. Day-One Checklist (Human) +## 16. Day-One Checklist (Human) When opening this repo for a new work session: diff --git a/README.md b/README.md index 71234c5..fd577fa 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,98 @@ # Pax-Automata -Autonomous agent for **Pax Historia** (browser grand-strategy). Perceives game state, reasons via a local “War Room,” and executes actions through Playwright. +An autonomous AI agent that plays [Pax Historia](https://www.paxhistoria.co) — a browser-based, AI-powered grand strategy game where you lead a nation through alternate history. -**Before making changes:** +## What is Pax Historia? -1. Read **`CLAUDE.md`** (constitution: tech stack, paths, git workflow, behavioral rules). -2. Check **`docs/project-tracker.md`** for current status. -3. Create a `feature/` or `fix/` branch; do not commit directly to `main`. +Pax Historia is a grand strategy sandbox where players pick a nation and a moment in history, then reshape the world through free-text actions, AI-driven diplomacy, and time-jumps. Every decision is processed by generative AI that simulates realistic geopolitical consequences. Think Civilization meets a creative writing partner. -**Design:** `docs/PRD.md` · **Flow:** `docs/diagrams/war-room-flow.mmd` (and `.png`). +## How It Works -**Scaffold:** `./scripts/scaffold.sh` — creates `war-room/` and `src/spy|brain|hand/`. +Pax-Automata runs a **cognitive loop** that mimics how a human player thinks: + +1. **Spy (Perception)** — Intercepts the game's network traffic to read the current world state: borders, battalions, events, diplomacy. +2. **War Room (Memory)** — Consults local files that define the agent's long-term goals, tactical playbook, and memory of ongoing operations. +3. **Brain (Reasoning)** — Sends all context to an LLM (Claude) which updates its strategic plans and generates a batch of actions. +4. **Hand (Execution)** — Types each action into the game's UI via browser automation, then triggers a time-jump to advance the world. + +This loop repeats every turn, building on previous plans and adapting to new events. + +## Architecture + +See `docs/diagrams/war-room-flow.mmd` for the full sequence diagram, or the rendered version at `docs/diagrams/war-room-flow.png`. + +``` +Browser (Pax Historia) + ↕ network intercept +Spy → current_state.json → Brain → ActionBatch → Hand → Browser + ↑ ↑ + constitution.md Claude API + crisis_handbook.txt + strategic_ledger.json +``` + +## Getting Started + +### Prerequisites + +- Node.js 18+ +- npm + +### Install + +```bash +git clone https://github.com/phillipyan300/Pax-Automata.git +cd Pax-Automata +npm install +``` + +### Build + +```bash +npm run build +``` + +### Run + +```bash +npm start +``` + +> The agent is under active development. Currently only the project scaffold is in place — no phases are implemented yet. See [Project Status](#project-status) below. + +## Project Status + +Tracked in [`docs/project-tracker.md`](docs/project-tracker.md). + +| Phase | Status | +|-------|--------| +| Phase 0: Project Init | Done | +| Phase 1: Perception (Spy) | In Progress | +| Phase 2: War Room | Pending | +| Phase 2.5: Advisor Query | Pending | +| Phase 3: Brain (LLM) | Pending | +| Phase 4: Hand (Execution) | Pending | + +## Tech Stack + +| Layer | Choice | +|-------|--------| +| Browser automation | Playwright | +| Runtime | Node.js + TypeScript | +| LLM | Anthropic Claude API | +| Validation | Zod | +| TS execution | tsx | +| Agent memory | JSON + Markdown files in `war-room/` | + +## Design Docs + +- **Product spec:** [`docs/PRD.md`](docs/PRD.md) +- **Sequence diagram:** [`docs/diagrams/war-room-flow.mmd`](docs/diagrams/war-room-flow.mmd) +- **Game research:** [`docs/pax-historia-research.md`](docs/pax-historia-research.md) + +## Contributing + +1. Read [`CLAUDE.md`](CLAUDE.md) — the project constitution (tech stack, paths, coding standards, git workflow). +2. Check [`docs/project-tracker.md`](docs/project-tracker.md) for current status. +3. Create a `feature/`, `fix/`, or `chore/` branch. Never commit directly to `main`. +4. The maintainer squash-merges into `main` after review. diff --git a/docs/auth-setup.md b/docs/auth-setup.md new file mode 100644 index 0000000..8726729 --- /dev/null +++ b/docs/auth-setup.md @@ -0,0 +1,159 @@ +# Auth setup (Google login for Pax Historia) + +The game at [paxhistoria.co](https://www.paxhistoria.co) uses Google login. Automating the login form with Playwright usually triggers Google’s bot detection (“This browser or app may not be secure”). So we **don’t automate login**; we **log in once manually** and reuse the saved auth state. + +--- + +## Demo: “login” with saved state (no password) + +To see that the saved state works, run: + +```bash +npm run demo-auth +``` + +That script **does not type a password or touch Google’s login form**. It: + +1. Reads **`auth/auth_state.json`** (cookies + localStorage). +2. Starts a new Playwright browser and **injects** that state into it. +3. Opens **paxhistoria.co**. The site sees the same cookies/localStorage as when you logged in, so it treats the browser as already logged in. +4. You should see the game or your dashboard instead of a login page. The script prints the page URL and title, then closes after a few seconds. + +So “logging in with the cookies” = **loading that JSON file into the browser before navigating**. No credentials are stored in code; they live only in `auth/auth_state.json` (and in the browser profile, see below). + +--- + +## What is `auth/profile/`? What must **not** be pushed to GitHub? + +| Item | What it is | Push to GitHub? | +|------|------------|-----------------| +| **`auth/auth_state.json`** | Snapshot of cookies + localStorage from one browser session. Used by the **game runner** and **demo** so they open already “logged in”. | **No.** Contains session tokens and identifiers. | +| **`auth/profile/`** | A **persistent browser profile** (like Chrome’s “user data” folder). Used **only by the capture script**. When you run `npm run capture-auth`, it opens this profile so you may already be logged in from last time; you only re-save when you want to refresh `auth_state.json`. | **No.** Contains cookies, cache, and other browser data. | +| **`auth_profile/`** (root) | Old location for the same kind of profile; ignored for new runs. | **No.** | + +The repo’s **`.gitignore`** already has: + +- **`auth/`** — ignores the whole directory (so `auth_state.json` and `profile/` never get committed). +- **`auth_profile/`** — ignores the legacy profile folder if it still exists. + +So **none of these should be pushed to GitHub**. If you ever commit them by mistake, treat the session as compromised: log out everywhere, re-capture auth state, and remove the secrets from git history (e.g. with `git filter-branch` or BFG). + +--- + +## What’s in `auth/auth_state.json`? + +Playwright’s **storage state** saves everything needed to restore a logged-in session: + +| Part | What it is | +|------|------------| +| **`cookies`** | Array of all cookies. Each has `name`, `value`, `domain`, `path`, **`expires`**, `httpOnly`, `secure`, `sameSite`. | +| **`origins`** | Per-origin **localStorage** (and sessionStorage if present). Sites like Pax Historia store tokens and app state here too. | + +So you get **cookies + localStorage** in one file. No need to copy anything by hand. + +### Where are the cookies? + +At the **top level** of the JSON: + +```json +{ + "cookies": [ + { "name": "SID", "value": "...", "domain": ".google.com", "path": "/", "expires": 1805061408.701884, ... }, + ... + ], + "origins": [ + { "origin": "https://www.paxhistoria.co", "localStorage": [ ... ] }, + ... + ] +} +``` + +### What does `expires` mean? + +**`expires`** is a **Unix timestamp in seconds** (decimal allowed). When that time passes, the cookie is no longer sent by the browser. + +- **Example:** `1805061408` → **2027-03-13** (Google “Stay signed in”–style cookies often last ~1 year). +- **Session cookies:** Sometimes stored as `-1` or a past date; they last until the browser/tab is closed. +- **To check a value:** In JS: `new Date(1805061408 * 1000)` or in the shell: `date -r 1805061408` (macOS). + +So yes: auth state saves **everything** (cookies + localStorage), the **cookies** are in the `cookies` array, and **expiration** is the `expires` number on each cookie. + +--- + +## Recommended: Auth state (log in once, reuse) + +### 1. Capture state (one-time or when it expires) + +```bash +npx tsx scripts/capture-auth-state.ts +``` + +- A **real browser window** opens (persistent profile, not headless). +- Go to the game and log in with Google (including the popup). +- When you’re fully in the game, switch back to the terminal and **press Enter**. +- The script saves cookies + localStorage to **`auth/auth_state.json`** (directory `auth/` is gitignored). + +### 2. Use it in your Playwright game runner + +When starting the browser for the agent, load that file so the session is already logged in: + +```ts +import { chromium } from "playwright"; + +const browser = await chromium.launch(); +const context = await browser.newContext({ + storageState: "auth/auth_state.json", +}); +const page = await context.newPage(); +await page.goto("https://www.paxhistoria.co/game/..."); +// You should already be logged in. +``` + +No need to look at the Network tab or copy cookies by hand; `storageState` captures everything Playwright needs. + +--- + +## How long does it last? + +- **Session cookies:** Until the browser session that created them would have closed (often hours). +- **Persistent cookies (e.g. “Stay signed in”):** Often weeks to months; Google and the game set their own expiry. +- **In practice:** Re-run the capture script whenever you get logged out or hit a login page again. The script uses a persistent profile (`auth/profile/`) so you can reopen and re-capture anytime. + +--- + +## Optional: Match your real Chrome (if you copy from Chrome) + +If you ever export cookies from **regular Chrome** and inject them into Playwright, the site may see a different **User-Agent** and treat the session as suspicious. To reduce that: + +1. In Chrome DevTools → **Console** run: `navigator.userAgent` and copy the string. +2. When creating the Playwright context, set the same string: + + ```ts + const context = await browser.newContext({ + storageState: "auth/auth_state.json", + userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...", // paste from Chrome + }); + ``` + +With the **capture script** (Playwright-only), the saved state already comes from a Playwright Chromium profile, so User-Agent usually matches and you don’t need this unless you hit issues. + +--- + +## Finding cookies manually (only if you need to debug) + +You don’t need this for the recommended flow. If you ever do: + +- **User-Agent:** DevTools → **Console** → `navigator.userAgent` +- **Cookies:** DevTools → **Application** → **Storage** → **Cookies** → select `https://www.paxhistoria.co` (and `accounts.google.com` if needed). Each row is one cookie; a single request sends all of them in one `Cookie:` header, semicolon-separated. Prefer using `storageState` instead of copying these by hand. + +--- + +## Summary + +| Approach | Use when | +|-----------------------|-----------------------------------| +| **Capture script** | Normal setup: one-time manual login, then reuse `auth/auth_state.json`. | +| **Persistent profile**| Same script uses `auth/profile/` so you can reopen and re-capture anytime. | +| **Manual cookie copy**| Only for debugging or when you can’t run the capture script. | + +Run `npm run capture-auth`, log in once, press Enter. State is saved under `auth/`. Use `storageState: 'auth/auth_state.json'` in your game runner. diff --git a/docs/project-tracker.md b/docs/project-tracker.md index 3a09c93..254a2f5 100644 --- a/docs/project-tracker.md +++ b/docs/project-tracker.md @@ -4,41 +4,136 @@ --- -## Current status (high level) +## Current Status (High Level) -| Area | Status | Notes | -|-------------------|----------|--------| -| Repo / docs setup | Done | PRD in docs, diagrams in docs/diagrams, CLAUDE.md and this tracker in place. | -| War Room scaffold | Done | `war-room/` and placeholder files in place. | -| Phase 1: Perception | Pending | Playwright Spy + `/api/simple-chat` → `current_state.json`. | -| Phase 2: War Room | Pending | constitution.md, crisis_handbook.txt, strategic_ledger.json. | -| Phase 2.5: Advisor | Pending | Query advisor chat, scrape response into context. | -| Phase 3: Brain | Pending | Assemble context, update ledger, filter by constitution, output batch of actions. | -| Phase 4: Hand | Pending | Batch execution via Playwright (action box submit). | +| Phase | Status | Description | +|-------|--------|-------------| +| Phase 0: Project Init | **Done** | Repo scaffold, Node+TS, Playwright, docs, CLAUDE.md | +| Phase 1: Perception | **In Progress** | Playwright Spy + `/api/simple-chat` → `current_state.json` | +| Phase 2: War Room | Pending | Populate constitution, handbook, ledger schema | +| Phase 2.5: Advisor | Pending | Query advisor chat, scrape response into context | +| Phase 3: Brain | Pending | Context assembly, LLM reasoning, action batch generation | +| Phase 4: Hand | Pending | Batch execution via Playwright (action box submit) | --- -## Done +## Phase 0: Project Init — Done -- [x] Reorganize repo: PRD → `docs/PRD.md`, diagrams → `docs/diagrams/war-room-flow.{mmd,png}`. -- [x] Add `CLAUDE.md` (constitution), `docs/project-tracker.md`, `docs/logs/`. -- [x] Add scaffold script (see `scripts/scaffold.sh`). -- [x] Initialize Node.js + TypeScript project (Playwright, ts-node, tsconfig, entry point). +- [x] Reorganize repo: PRD → `docs/PRD.md`, diagrams → `docs/diagrams/` +- [x] Add `CLAUDE.md` (constitution), `docs/project-tracker.md`, `docs/logs/` +- [x] Add scaffold script (`scripts/scaffold.sh`) +- [x] Initialize Node.js + TypeScript project (Playwright, tsx, Zod, tsconfig) +- [x] Create `war-room/` placeholders (constitution.md, crisis_handbook.txt, strategic_ledger.json, current_state.json) +- [x] Create `src/` skeleton (spy/, brain/, hand/, index.ts) +- [x] Consolidate game research into `docs/pax-historia-research.md` +- [x] Lock CLAUDE.md: tech stack, coding standards, folder structure, data-flow contracts --- -## Pending +## Phase 1: Perception — In Progress -- [x] Run `./scripts/scaffold.sh` to create `war-room/` and placeholders. -- [x] Choose runtime (Node/TS recommended) and add package manager + base deps. -- [ ] Phase 1: Implement Perception layer (Spy + current_state.json). -- [ ] Phase 2: Populate War Room templates (constitution, handbook, ledger schema). -- [ ] Phase 2.5: Implement advisor query + response scrape. -- [ ] Phase 3: Implement Brain (context assembly, ledger update, action batch). -- [ ] Phase 4: Implement Hand (batch execution in browser). +**Goal:** Intercept `/api/simple-chat` responses and write parsed game state to `current_state.json`. + +**Files to create/modify:** +- `src/shared/schemas.ts` — Zod schema for GameState +- `src/shared/index.ts` — barrel export +- `src/spy/interceptor.ts` — Playwright route handler for `/api/simple-chat` +- `src/spy/state-parser.ts` — parse raw API response → validated GameState +- `src/spy/state-writer.ts` — write GameState to `war-room/current_state.json` +- `src/spy/index.ts` — barrel export + +**Sub-tasks:** +- [ ] Capture a real `/api/simple-chat` response (need DevTools network tab — human task) +- [ ] Define Zod GameState schema from captured payload +- [ ] Implement route interceptor (Playwright `page.route()`) +- [ ] Implement state parser (raw JSON → Zod-validated GameState) +- [ ] Implement state writer (GameState → `war-room/current_state.json`) +- [ ] Wire into `src/index.ts` entry point +- [ ] Smoke test: launch browser, trigger a game event, verify `current_state.json` is written + +--- + +## Phase 2: War Room — Pending + +**Goal:** Populate the War Room template files with real strategic content and validate the ledger schema. + +**Files to create/modify:** +- `war-room/constitution.md` — long-term goals (populate with real content) +- `war-room/crisis_handbook.txt` — tactical playbook (populate with real content) +- `src/shared/schemas.ts` — Zod schema for StrategicLedger (add to existing) +- `src/brain/war-room-reader.ts` — read + validate all War Room files + +**Sub-tasks:** +- [ ] Write constitution content (long-term goals, identity, constraints) +- [ ] Write crisis handbook content (tactical doctrines, procedures) +- [ ] Define Zod StrategicLedger schema +- [ ] Implement War Room reader (read all files, validate, return typed context) + +--- + +## Phase 2.5: Advisor — Pending + +**Goal:** Query the in-game advisor and scrape the response into the Brain's context. + +**Files to create/modify:** +- `src/hand/advisor.ts` — type query into advisor box, wait for response, extract text +- `src/hand/index.ts` — barrel export + +**Sub-tasks:** +- [ ] Identify advisor box selectors (need DevTools — human task) +- [ ] Implement advisor query function (type → submit → wait → scrape response) +- [ ] Add advisor response to Brain context assembly + +--- + +## Phase 3: Brain — Pending + +**Goal:** Assemble all context, call Claude API, generate a validated batch of actions. + +**Files to create/modify:** +- `src/brain/context-assembler.ts` — read all inputs, build prompt +- `src/brain/llm-client.ts` — Anthropic Claude API wrapper +- `src/brain/action-generator.ts` — call LLM, parse + validate ActionBatch +- `src/shared/schemas.ts` — Zod schema for ActionBatch (add to existing) +- `src/brain/index.ts` — barrel export + +**Sub-tasks:** +- [ ] Define ActionBatch Zod schema +- [ ] Implement context assembler (GameState + War Room files + advisor → prompt) +- [ ] Implement Claude API client +- [ ] Implement action generator (prompt → ActionBatch) +- [ ] Update strategic_ledger.json with new plans after generation + +--- + +## Phase 4: Hand — Pending + +**Goal:** Execute the action batch by typing each action into the game UI. + +**Files to create/modify:** +- `src/hand/executor.ts` — iterate ActionBatch, type + submit each action +- `src/hand/index.ts` — barrel export (update) + +**Sub-tasks:** +- [ ] Identify action box submit selectors/mechanics (need DevTools — human task) +- [ ] Implement batch executor (loop through actions, type + submit) +- [ ] Implement time-jump trigger +- [ ] Final ledger commit after execution +- [ ] Wire full cognitive loop in `src/index.ts` + +--- + +## Blocking / Human Tasks + +These require a human with a browser and DevTools open: + +- [ ] Capture `/api/simple-chat` JSON payload (Phase 1 blocker) +- [ ] Identify diplomacy chat selector +- [ ] Identify timeline/jump selector +- [ ] Determine submit mechanics for each UI element (button? Enter? both?) --- ## Logs -Timestamped writeups go under `docs/logs/` (e.g. `2026-02-07-scaffold.md`, `2026-02-08-perception-debug.md`). +Timestamped writeups go under `docs/logs/` (e.g. `2026-02-07-scaffold.md`). diff --git a/package.json b/package.json index 17ca444..24abbed 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "main": "dist/index.js", "scripts": { "build": "tsc", - "start": "ts-node src/index.ts", + "start": "tsx src/index.ts", + "capture-auth": "tsx scripts/capture-auth-state.ts", + "demo-auth": "tsx scripts/demo-auth-state.ts", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { diff --git a/scripts/capture-auth-state.ts b/scripts/capture-auth-state.ts new file mode 100644 index 0000000..312ca44 --- /dev/null +++ b/scripts/capture-auth-state.ts @@ -0,0 +1,63 @@ +/** + * Capture auth state for Pax Historia (Google login). + * + * Opens a real browser window. You log in manually (including the Google popup). + * When you're in the game, press Enter in the terminal to save cookies + localStorage + * to auth/auth_state.json. Your main script should use storageState: 'auth/auth_state.json'. + * + * Run: npm run capture-auth (or npx tsx scripts/capture-auth-state.ts) + */ + +import { chromium } from "playwright"; +import * as fs from "fs"; +import * as readline from "readline"; +import * as path from "path"; + +const AUTH_DIR = path.join(process.cwd(), "auth"); +const GAME_URL = "https://www.paxhistoria.co"; +const USER_DATA_DIR = path.join(AUTH_DIR, "profile"); +const STATE_PATH = path.join(AUTH_DIR, "auth_state.json"); + +async function main() { + console.log("-------------------------------------------"); + console.log("Pax Historia — Auth state capture"); + console.log("-------------------------------------------"); + console.log("1. A browser window will open."); + console.log("2. Log in to the game (use 'Sign in with Google' if needed)."); + console.log("3. When you see the game and are fully logged in, come back here."); + console.log("4. Press ENTER in this terminal to save auth state and exit."); + console.log("-------------------------------------------\n"); + + fs.mkdirSync(AUTH_DIR, { recursive: true }); + + const context = await chromium.launchPersistentContext(USER_DATA_DIR, { + headless: false, + slowMo: 50, + args: [ + "--disable-blink-features=AutomationControlled", + "--start-maximized", + ], + }); + + const page = context.pages()[0] ?? (await context.newPage()); + await page.goto(GAME_URL, { waitUntil: "domcontentloaded" }); + + await new Promise((resolve) => { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + rl.question("Press ENTER after you are logged in to save auth state... ", () => { + rl.close(); + resolve(); + }); + }); + + await context.storageState({ path: STATE_PATH }); + await context.close(); + + console.log(`\nAuth state saved to: ${STATE_PATH}`); + console.log("Use it in your runner: newContext({ storageState: 'auth/auth_state.json' })"); +} + +main().catch((err) => { + console.error("Error:", err); + process.exit(1); +}); diff --git a/scripts/demo-auth-state.ts b/scripts/demo-auth-state.ts new file mode 100644 index 0000000..cb38943 --- /dev/null +++ b/scripts/demo-auth-state.ts @@ -0,0 +1,51 @@ +/** + * Demo: load saved auth state and open Pax Historia (no manual login). + * + * Uses auth/auth_state.json. If you're logged in, you should see the game + * or your dashboard instead of a login page. + * + * Run: npx tsx scripts/demo-auth-state.ts + */ + +import { chromium } from "playwright"; +import * as path from "path"; +import * as fs from "fs"; + +const AUTH_DIR = path.join(process.cwd(), "auth"); +const STATE_PATH = path.join(AUTH_DIR, "auth_state.json"); +const GAME_URL = "https://www.paxhistoria.co"; + +async function main() { + if (!fs.existsSync(STATE_PATH)) { + console.error("No auth state found. Run: npm run capture-auth"); + process.exit(1); + } + + console.log("Loading saved auth state from auth/auth_state.json ..."); + const browser = await chromium.launch({ headless: false }); + const context = await browser.newContext({ + storageState: STATE_PATH, + }); + + const page = await context.newPage(); + console.log("Navigating to", GAME_URL, "..."); + await page.goto(GAME_URL, { waitUntil: "domcontentloaded", timeout: 15000 }); + + await page.waitForTimeout(3000); + + const url = page.url(); + const title = await page.title(); + console.log("\n--- Result ---"); + console.log("URL:", url); + console.log("Title:", title); + console.log("If you see the game (not a login page), auth state is working."); + console.log("Browser will close in 5 seconds ..."); + + await page.waitForTimeout(5000); + await browser.close(); +} + +main().catch((err) => { + console.error("Error:", err); + process.exit(1); +});