MicroAI Paygate
An x402-style payment gateway for AI requests. Sign EIP-712, get a signed receipt, verify the signature client-side.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗This is a system for tracking and managing the documentation and reimbursement requests for the Florida StepUp For Students program. It is available at https://stepup.wooleys.us
This is a system built to solve the problem of keeping personal records for the reimbursement process of StepUp for Students. It is designed to make it easy to pull together all of the information required to submit for reimbursement and to streamline the submission process.
When you first visit this site asks you to log in to your personal Microsoft OneDrive account. It then saves all of your data to a folder structure in OneDrive. This site never sees your data all of your data only ever goes from your computer to your OneDrive.
(Yes this part is AI Written)
In addition to the web interface there is also a semi-automated Playwright helper for filing reimbursement requests on StepUp For Students, driven by the data on OneDrive. It:
Status = "Unfiled (Ready to Submit)") directly from your OneDrive data via the Microsoft Graph API — not a downloaded copy — so it's always current and safe to use even while other collaborators have the sheet open.<year>/_stepup/reimbursements/<number>.json, in the folder of the school year it belongs to (Excel years included; _stepup is not _ledger, so it does not turn a year into a ledger year), with a small <year>/_stepup/index.json summary. Only reimbursements whose content changed are written, so a fresh copy costs one small download per year and a few small uploads. The web app reads the index: an Excel year is enhanced with it (statuses, what StepUp said, amounts and ids the sheet left blank, reimbursements the sheet never recorded), and a year with only _stepup shows as StepUp history, read-only, so a new person gets their whole history from one sync. A year folder is created if StepUp has reimbursements for a year that has none. Each fresh copy also brings every year's own record up to date: a reimbursement goes to its school year's ledger, or to that year's workbook (only workbooks with Reimbursement ID and Line Number columns, which today is 2025-2026; the older layouts are reported and skipped). In the web app StepUp's data outranks the ledger, which outranks Excel, except for something done here after StepUp was last read. npm run statuses -- --from-capture re-applies the last copy kept in .cache/ to every year without a login.Table5 (Categories) current, at most once per 24h: StepUp's Category/Type/Detail picker APIs can't be called from scratch by us (they need an auth token only the page's own JS attaches), so instead — the first time you (or our own Category-filling step) naturally trigger one of those requests that day — we intercept it and piggyback an additional out-of-band request asking for every ID we know about so far, reusing that request's real auth, chunked and lightly throttled between chunks. The original request is always left completely untouched, so the page's own UI never sees anything different. Results accumulate in .cache/category-tree-cache.json across days, so full tree coverage typically only takes a couple of real Category clicks to bootstrap, not months of incidental exposure..cache/vendor-listing-cache.json), from the "Who did you pay?" dropdown's own API responses whenever they naturally load — local cache only for now, not yet synced into a spreadsheet table.Pre-Auth status (matched by Pre-Auth #) from StepUp's Pre-Authorization list API, same throttled pattern — currently dormant since no rows have a Pre-Auth # recorded yet. (Full Pre-Auth submission automation — a separate wizard from reimbursements — isn't built yet; see todo.md.)Most of what used to need npm run … is now in the web app's Advanced menu and Summary tab, using the same shared code:
verify command. The result is a panel on the Summary tab.backup command, as one download with a manifest of SHA-256 hashes.plan command, on the Summary tab.stepup-bookmarklet.js from this site, so updates need no new bookmark. Your StepUp sign-in never leaves StepUp's page._stepup-cache folder in the workspace. The bookmark makes exactly one request of its own (the reimbursements list); asking for the category list, providers and students itself is an off-by-default setting. Advanced → Get the StepUp bookmarklet shows the connection and can record which calls StepUp makes (field names only), which is how the remaining CLI features get built..cache/stepup-search-responses.json, or a raw response) and every year's _stepup folder, History and the open year's ledger are brought up to date: the statuses --from-capture command, with no login.What still needs the command line is everything that talks to StepUp itself (filing, live status sync, learning categories and
vendors), because only a program running next to StepUp's own page can. The plan for replacing that with a browser extension
is in docs/EXTENSION_PLAN.md.
npm workspaces monorepo. Everything is still run from the repo root, so .env, .cache/, .chrome-profile/ and data/ stay where they were.
packages/cli/ — the Playwright automator (everything described below). Node-only: MSAL device-code auth, disk caches, browser automation.packages/shared/ — environment-agnostic code used by both sides: the Microsoft Graph/Excel client (token supplied by the host via setTokenProvider), workbook table names, status constants and category parsing.packages/web/ — placeholder for the static GitHub Pages data-entry portal (not built yet).Run npm run typecheck to check every package.
npm install
The StepUp browser session (npm run inspect, npm start) launches your real, installed Google Chrome through a dedicated profile at .chrome-profile/ (gitignored, separate from your everyday Chrome profile) — not a bundled/downloaded browser. (We looked into getting Apple Passwords autofill working in that profile too; it ran into a few layers of Chrome restrictions around automated sessions that weren't worth fighting for what it saved, so credentials just get typed manually — you're already doing that for StepUp's own login regardless.)
We tried skipping this by reusing Microsoft's own well-known public client IDs (Graph PowerShell, VS Code, OneDrive sync, Office) for device-code sign-in. All four failed for a personal Microsoft account: some reject personal accounts outright, and the rest get flagged by Microsoft's anti-phishing protections (device-code flow with a well-known high-trust client ID is a known phishing pattern, so Microsoft blocks/warns on it for personal accounts). So: register your own app. It's free and only takes a few minutes.
Files.ReadWrite, Files.ReadWrite.All, offline_access → Save. (No admin consent needed — you'll consent yourself on first sign-in.)Because it's genuinely your own app, device-code sign-in won't look anomalous to Microsoft the way reusing a well-known client ID did.
Browser note: do the device-code sign-in in Chrome or Edge, not Safari — Safari's cross-site tracking prevention breaks the multi-domain redirect chain Microsoft's login flow relies on and produces confusing, unrelated-looking errors.
cp .env.example .env
Edit .env and paste in the Client ID from step 2.6. The authority and both OneDrive links are already filled in.
npm run discover
First run will print a Microsoft device-login link + code — open it in Chrome/Edge, sign in as yourself, approve. After that it's cached (.cache/, gitignored) and you won't be asked again for months of normal use. This also prints your workbook's table/worksheet/column names and the reference-files folder contents — useful if the spreadsheet's structure ever changes and the code needs updating to match.
The storage is chosen behind one interface (StorageProvider), so a workspace can live in OneDrive, in Google Drive, or
only in the browser / a local folder. Google Drive needs a Google Cloud project once:
In Google Cloud Console create a project and enable the Google Drive API.
OAuth consent screen: app name, support email, the privacy policy (https://stepup.wooleys.us/privacy.html) and terms
(/terms.html) links, and the authorized domain wooleys.us. Add the scopes .../auth/drive.file and .../auth/drive.appdata.
Both are non-sensitive, so the app can be published without Google's verification review. (The full drive scope is
"restricted" and is not used.)
Website: create an OAuth client of type Web application with https://stepup.wooleys.us (and http://localhost:5173
for development) as authorized JavaScript origins. Put its client ID in GOOGLE_CLIENT_ID in
packages/web/src/googleAuth.ts (it is public, like the Microsoft one). Until it is set, "Sign in with Google" is not shown.
Command line (optional): create a second client of type Desktop app and set STEPUP_STORAGE=google,
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET and GOOGLE_FILES_FOLDER_URL in .env (see .env.example). The first run opens
a browser to sign in; the refresh token is kept in .cache/google-token.json.
Folder picker (what lets the non-sensitive drive.file scope open a folder the app did not create): enable the Google
Picker API, create a browser API key restricted to the site's origins and to the Picker API, and put it and the
project number in GOOGLE_API_KEY and GOOGLE_APP_ID in packages/web/src/googleAuth.ts (both are public). Until they are
set the "Choose an existing folder with Google Drive" button is not shown.
Limits on Google Drive: with the non-sensitive drive.file scope the app only sees files and folders it created or that were
chosen in the picker; it cannot browse your Drive. Files you drop into a folder by hand are not visible to it unless picked too.
Years still kept in an Excel workbook cannot be viewed (that uses OneDrive's Excel service), and the
single-session lock is best effort because Drive has no conditional writes.
npm start
No arguments — it processes every unfiled row in one run, in groups. For each group it will:
You can type skip at the start of any group to move to the next one without processing it.
One-time spreadsheet change: Table1 now has a Line Number column (added right after Reimbursement ID) — a submission's Reimbursement ID is shared across all its rows, but each row's specific line item within that submission (Reimbursement ID + Line Number, e.g. 35958661 + 1) is what StepUp's status API actually keys on, which is what lets status-sync update rows individually instead of only in whole-submission batches. Table3 (Statuses) also needs a Paid entry added — StepUp's API reports it as distinct from Approved, but it wasn't in the original list; add it yourself in the spreadsheet whenever convenient before status-sync needs to use it.
id (e.g. item_0, category) across every repeated instance — a bug on their end. Fields are targeted positionally (.nth(i)), not by plain #id.A year folder that has its tracking workbook but no _ledger/ shows in the web app's year list as (Excel, read-only).
Opening it reads the workbook and the receipts folder through OneDrive each time (nothing is stored as truth), so Excel stays
the authority and edits made there appear on the next open or Refresh from Excel. It has its own read-only
Purchases, Proof of payment and Summary pages (the Summary has two pivot tables to group and compare by student, store, status and more), and a spreadsheet download built on your device; nothing in it can be
changed and nothing is written to OneDrive except one small file, Step Up Helper - receipt choices.json, in the year folder.
A year folder may hold several copies of the workbook; the most recently edited one is used (the banner says which). Three
layouts are understood: the 2025-2026 sheet (files named in "Documentation File" columns), the 2024-2025 sheet, and the
2023-2024 Gardiner sheet, whose rows have no file columns, so a file is attached to a row only when its name's date and
vendor agree. Receipts are looked for in the year folder and its sub-folders; files nothing points to are listed apart.
It remembers which file you said is the receipt when the sheet lists several and none is clearly it (until you pick, every
file shows as additional documentation). The code is isolated in packages/shared/src/legacy/ and
packages/web/src/legacyYear.ts, and can be removed once the year is settled.
Everything year-specific lives in .env, not code:
ONEDRIVE_EXCEL_URL and ONEDRIVE_FILES_FOLDER_URL to that year's workbook/folder share links.npm run discover to confirm the table/column names still match what packages/cli/src/reimbursements.ts expects (Table1, Status, Documentation File 1-6, etc.) — if StepUp's site changed its field IDs or your spreadsheet's columns were renamed, packages/cli/src/ would need updating too, but the links themselves never require a code change.The web app's History tab shows every change, from every source: what was done in the app (from the ledger's event log),
what StepUp itself changed (a status, an amount, a reason; recorded by the CLI whenever a fresh copy of its list differs from
what was kept), and what changed in the Excel workbook (by a person, or by the status sync; recorded from before-and-after
readings). StepUp's and Excel's changes are kept as small files in <year>/_history/. The workbook is followed from the first
sync that reads it, so edits made before then are not in the history. Only workbooks with a Table1 are followed (not the
2023-2024 Gardiner sheet).
.env, .cache/ (your auth token cache), .chrome-profile/, and data/ (downloaded receipts, temporary) are all gitignored — don't commit them.npm start still files from the Excel workbook exactly as before. To file from a ledger year instead:
npm start -- --ledger=2026-2027
npm run reference:build still folds the local cache into the shared list.npm run report -- 2026-2027 does it on demand.StepUp misbehaves when two people (or two sessions) use the same account, so npm start (either mode) and npm run inspect take a lock first: a small stepup-session.lock.json in the shared workspace folder, refreshed every minute and removed on exit or Ctrl+C. If someone else holds it, you are told who, from which machine, and since when, and the run stops. A lock whose owner has stopped refreshing it (crash, sleeping laptop) is treated as abandoned after 5 minutes. npm start -- --force-unlock takes over a lock you are sure is a leftover; the other session is told at its next refresh.
.cache/drafts.json, so either person, on any machine, is offered to resume it. The spreadsheet mode still uses .cache/drafts.json.npm run inspect) print field names and selectors, never field values.data/ (downloaded receipts), .cache/ (sign-in tokens and local caches) and .env are git-ignored, and a secret scan (gitleaks) runs on every push. Do not paste real statement text or receipts into issues; the "share category fixes" export contains only category names and ids.npm run verify -- 2026-2027 checks the ledger is consistent (nothing points at something missing, no clashing events, no duplicate files, clocks sane). Add --files to confirm every file is still in OneDrive at the recorded size, --hash to also compare each file's SHA-256.npm run backup -- 2026-2027 --to ~/Backups/stepup [--documents] copies the event logs, the category list and the spreadsheet (and, with --documents, every file) outside OneDrive, with a manifest of SHA-256s.