Adopt shared-SA model, add DEPLOY guide, MIT license

- Install flow now assumes one admin-distributed service account JSON shared
  across the team. Each user creates their own Google Sheet and shares it
  with the SA email; per-user GCP project setup is gone.
- DEPLOY.md: a step-by-step walkthrough for non-technical users following
  along with an AI agent (Terminal basics, screenshots-worth of expected
  output, common-error table).
- scripts/append_row.py: sheet_id field accepts either a bare ID or a full
  Google Sheets URL; normalize_sheet_id() extracts the ID via regex.
- scripts/setup.md: rewritten as an admin guide (one-time GCP setup, key
  rotation) plus a troubleshooting reference.
- LICENSE: MIT (previously "private — internal use"). README license
  sections updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 11:08:48 +08:00
parent 499af13bd3
commit 4e40772150
8 changed files with 514 additions and 129 deletions

View File

@@ -1,39 +1,102 @@
# One-time setup
# Admin setup + troubleshooting
## 1. Python deps
```
pip install google-api-python-client google-auth
```
End users follow the 4-step install in the main README (or the gentler [`DEPLOY.md`](../DEPLOY.md)). This page covers two things they don't see:
## 2. Google Cloud service account
1. Create (or reuse) a GCP project.
2. Enable the **Google Sheets API** for the project.
3. Create a **service account**; skip the optional IAM steps.
4. In the service account, create a **JSON key** and download it.
5. Move the key to a safe path, e.g. `~/.config/gcp/autoacct-sa.json`, then:
```
chmod 600 ~/.config/gcp/autoacct-sa.json
```
1. **Admin setup** — what you do once, before anyone can install
2. **Troubleshooting** — the errors users will report back to you
## 3. Prepare the Google Sheet
1. Create a new Google Sheet (or open an existing one).
2. Rename the first tab to `Expenses` (or update `worksheet` in config).
3. In row 1 add headers matching `schema.md` columns AN:
`Date | Merchant | Category | Amount | Currency | Amount (HKD) | FX Rate | FX Date | Payment Method | Line Items | Raw OCR | Note | Receipt | Logged At`
4. Open the service account JSON and copy the `client_email` value (looks like `...@...iam.gserviceaccount.com`).
5. Click **Share** on the sheet and add that email as **Editor**.
6. Copy the sheet ID from the URL: `https://docs.google.com/spreadsheets/d/<SHEET_ID>/edit`.
---
## 4. Skill config
```
cd ~/.openclaw/workspace/skills/AutoACCT
cp config.example.json config.json
# edit config.json: sheet_id, service_account_path
```
## Part A — Admin setup (one time)
## 5. Sanity check
```
echo '{"date":"2026-04-20","merchant":"TEST","category":"Other","amount":1,"currency":"HKD","amount_hkd":1,"fx_rate":1,"fx_date":"2026-04-20"}' \
| python ~/.openclaw/workspace/skills/AutoACCT/scripts/append_row.py
```
You should see `OK Expenses!A2:N2` (or similar) and a new row in the sheet. Delete the TEST row when done.
The shared-key model: you create **one** Google Cloud service account and distribute its JSON key + email address to every user. Each user creates their own Google Sheet and shares it with that one service-account email. The service account can only write to sheets that have been explicitly shared with it.
### A.1 — Create the Google Cloud project + service account
1. Go to https://console.cloud.google.com/ and create a new project (e.g. `autoacct`).
2. In the top search bar, search **Google Sheets API** → click the result → **Enable**.
3. Left menu: **IAM & Admin → Service Accounts → + Create Service Account**.
- Name: `AutoACCT` (any name works)
- Click **Create and Continue → Done** (skip the optional IAM role step — the service account doesn't need any GCP roles, since it gains write access per-sheet via sheet-level sharing).
4. Click the new service account → **Keys** tab → **Add Key → Create new key → JSON → Create**.
A `.json` key file downloads to your browser's Downloads folder.
5. **Copy the service account's email** (e.g. `autoacct@<project>.iam.gserviceaccount.com`).
6. Rename the downloaded file to `autoacct-sa.json` (recommended — DEPLOY.md assumes this name).
### A.2 — Distribute the key + email to users
The JSON is a private key. Use a secure channel:
| Channel | Verdict |
|---|---|
| **Password manager shared vault** (1Password, Bitwarden, Vaultwarden) | **Recommended.** Easy to revoke, no copies floating in inboxes. |
| Encrypted email / Signal / private DM | OK for small teams. |
| Cloud drive with strict per-user ACLs | OK if your org already uses one. |
| **Plain email** | Do not. |
| **Git repo (even private)** | Do not — `.gitignore` already excludes `*-sa.json`. |
Tell each user:
- The JSON file (attach / share)
- The service-account email (so they know who to share their sheet with)
- Pointer to [`DEPLOY.md`](../DEPLOY.md) if they want hand-holding, or [`README.md`](../README.md) if they're comfortable with the terminal
### A.3 — Verify your own install first
Before sending the JSON to anyone, run through the user-side install yourself (Steps 111 of `DEPLOY.md`) to confirm everything works end-to-end. It's also a chance to catch any GCP-side misconfiguration before users hit it.
### A.4 — Key rotation (when you need to)
Rotate the JSON key when:
- A user leaves the team (so you stop trusting their copy of the key)
- You suspect a leak
- Every 612 months as routine hygiene
To rotate:
1. In GCP Console → Service Accounts → keys → **Add Key** (creates a new one) → download.
2. **Delete the old key** in the same panel. (After deletion, any existing copies stop working.)
3. Re-distribute the new JSON to all current users via your secure channel.
4. Users replace their `~/.config/gcp/autoacct-sa.json` with the new file. No other changes needed — `config.json`, sheet sharing, etc. all stay intact.
---
## Part B — Troubleshooting (user errors you'll see)
### `HTTP 403` / `The caller does not have permission`
The user forgot to share their sheet with the service-account email, or typed the email wrong. Tell them to re-do Step 8 in DEPLOY.md (or Step 3.5 in README.md). Confirm the email you sent them matches exactly.
### `HTTP 400: Unable to parse range`
The `worksheet` value in `config.json` doesn't match the actual tab name. Most common cause: user has Chinese Sheets UI → tab is `工作表1`, but they wrote `"worksheet": "Sheet1"`. Fix the config.
### `HTTP 404` / `Requested entity was not found`
`sheet_id` in `config.json` is wrong. Tell user to re-copy the long string from `/d/.../edit` in their browser's URL bar.
### `FileNotFoundError ... autoacct-sa.json`
The JSON file isn't where `config.json` expects. Common causes:
- User saved with a different filename (e.g. `autoacct-project-12345.json`) and never renamed it.
- User skipped the `mv` step and the file is still in Downloads.
Run `ls -la ~/.config/gcp/` to check.
### `ImportError: No module named 'googleapiclient'`
Python deps not installed. Run `pip install google-api-python-client google-auth`. If `pip` is missing, try `pip3` or `python3 -m pip install ...`.
### `config.json not found`
User skipped the `cp config.example.json config.json` step. They need to be inside the skill directory when running it.
### JSON parse error (`Expecting value` / `Extra data`)
Smart quotes from TextEdit, or a stray comma. Fix in plain-text mode, or have an AI agent repair the file.
### Authorization or quota errors at scale
The shared SA shares one GCP project's quota. The default Sheets API quota (300 req/min per project) is generous for receipts — you would have to log thousands per minute to hit it. If you do hit quota, request an increase in the GCP console.
---
## Part C — When to abandon the shared-key model
The shared-key model is right for **trusted internal teams** (you know everyone with a copy of the JSON). Move to a different model if any of these happen:
- **You're distributing to strangers / customers.** They can write to each other's sheets if the JSON leaks. Move to OAuth (each user authenticates with their own Google account).
- **You can't trust users to keep the JSON private.** Same answer.
- **You need per-user audit trails.** Sheets API logs only "the SA wrote" — you can't tell from GCP which user did it. (Sheet revision history still shows it, since each user has their own sheet.)
In those cases, see the git history for an earlier Apps Scriptbased variant, or design a small backend that holds the key server-side and exposes a per-user-authenticated endpoint.