@pplethai/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +161 -0
  2. package/askpass/pple-askpass.mjs +18 -0
  3. package/dist/api.js +62 -0
  4. package/dist/bin.js +16 -0
  5. package/dist/cli.js +103 -0
  6. package/dist/commands/auth.js +32 -0
  7. package/dist/commands/deploy.js +106 -0
  8. package/dist/commands/init.js +77 -0
  9. package/dist/commands/upgrade.js +29 -0
  10. package/dist/credentials.js +53 -0
  11. package/dist/deps.js +87 -0
  12. package/dist/device-flow.js +115 -0
  13. package/dist/errors.js +16 -0
  14. package/dist/git.js +127 -0
  15. package/dist/link.js +49 -0
  16. package/dist/node-deps.js +84 -0
  17. package/dist/npm.js +127 -0
  18. package/dist/scaffold.js +58 -0
  19. package/dist/session.js +29 -0
  20. package/dist/skills.js +86 -0
  21. package/dist/ui.js +12 -0
  22. package/dist/update.js +142 -0
  23. package/package.json +35 -0
  24. package/skills/pple-platform/SKILL.md +102 -0
  25. package/skills/pple-platform/evals/evals.json +29 -0
  26. package/skills/pple-platform/references/deployment.md +61 -0
  27. package/skills/pple-platform/references/design-system.md +50 -0
  28. package/skills/pple-platform/references/mini-app.md +74 -0
  29. package/skills/pple-platform/references/pple-id-auth.md +85 -0
  30. package/skills/pple-platform/references/project-scaffold.md +75 -0
  31. package/templates/golden-path/.dev.vars.example +17 -0
  32. package/templates/golden-path/.env.example +8 -0
  33. package/templates/golden-path/.github/workflows/ci.yml +22 -0
  34. package/templates/golden-path/CLAUDE.md +60 -0
  35. package/templates/golden-path/README.md +60 -0
  36. package/templates/golden-path/gitignore +6 -0
  37. package/templates/golden-path/index.html +16 -0
  38. package/templates/golden-path/migrations/0001_init.sql +9 -0
  39. package/templates/golden-path/package-lock.json +4283 -0
  40. package/templates/golden-path/package.json +38 -0
  41. package/templates/golden-path/shared/api.ts +56 -0
  42. package/templates/golden-path/src/auth.ts +53 -0
  43. package/templates/golden-path/src/db.ts +32 -0
  44. package/templates/golden-path/src/env.ts +19 -0
  45. package/templates/golden-path/src/files.ts +38 -0
  46. package/templates/golden-path/src/index.ts +104 -0
  47. package/templates/golden-path/src/notify.ts +64 -0
  48. package/templates/golden-path/test/apply-migrations.ts +4 -0
  49. package/templates/golden-path/test/env.d.ts +17 -0
  50. package/templates/golden-path/test/harness.ts +57 -0
  51. package/templates/golden-path/test/smoke.test.ts +155 -0
  52. package/templates/golden-path/tsconfig.json +20 -0
  53. package/templates/golden-path/tsconfig.worker.json +21 -0
  54. package/templates/golden-path/vite.config.ts +29 -0
  55. package/templates/golden-path/vitest.config.ts +26 -0
  56. package/templates/golden-path/web/App.tsx +250 -0
  57. package/templates/golden-path/web/NotInApp.tsx +23 -0
  58. package/templates/golden-path/web/app.css +112 -0
  59. package/templates/golden-path/web/main.tsx +40 -0
  60. package/templates/golden-path/web/pple.ts +73 -0
  61. package/templates/golden-path/web/vite-env.d.ts +10 -0
  62. package/templates/golden-path/wrangler.jsonc +53 -0
@@ -0,0 +1,85 @@
1
+ # PPLE ID authentication (Builder Apps)
2
+
3
+ PPLE ID is the party's OIDC identity provider. For a **mini-app**, the flow is:
4
+
5
+ ```
6
+ PPLE Today ──opens mini-app in WebView──▶ web (React)
7
+ web: PPLEMiniApp.init() → getAccessToken() ── bearer token
8
+ web ──Authorization: Bearer <token>──▶ Worker
9
+ Worker ──GET {SSO_BASE_URL}/oidc/v1/userinfo──▶ PPLE ID
10
+ ```
11
+
12
+ The Worker never sees a password and holds no session; the bearer token is
13
+ the credential on every request (`src/auth.ts`).
14
+
15
+ Tokens also exist outside mini-apps for **Builders** (Console browser login,
16
+ `pple auth login` device flow). That does not change end-user auth for the
17
+ app you are building: do not invent a browser sign-in for the mini-app itself.
18
+
19
+ ## Three ways the mini-app can start
20
+
21
+ Canonical wiring: `web/pple.ts` + `web/main.tsx` + `web/NotInApp.tsx` in the
22
+ golden-path template.
23
+
24
+ 1. **Local `vite dev`** — set `token` to the `DEV_AUTH` value from `.dev.vars`
25
+ and treat the session as signed-in. No PPLE Today required.
26
+ 2. **Inside PPLE Today** — detect the mini-app user agent, init
27
+ `@pplethai/pple-today-miniapp-sdk`, capture the access token from the
28
+ launch URL, attach it on every `api()` call.
29
+ 3. **Plain browser (production)** — show the friendly NOT_IN_APP screen
30
+ ("เปิดในแอป PPLE Today"). There is no browser login for mini-apps.
31
+
32
+ ```tsx
33
+ import { isInMiniAppUserAgent } from "@pplethai/components";
34
+ import { PPLEMiniApp } from "@pplethai/pple-today-miniapp-sdk";
35
+
36
+ // Local: import.meta.env.DEV → DEV_AUTH token ("dev-token" by default)
37
+ // Plain browser: !isInMiniAppUserAgent → { kind: "not-in-app" }
38
+ // Inside Today: sdk.init() → getAccessToken() → { kind: "signed-in" }
39
+ ```
40
+
41
+ OAuth client values (`VITE_OAUTH_URL`, `VITE_OAUTH_CLIENT_ID`,
42
+ `VITE_OAUTH_REDIRECT_URI`) are provisioned per app and injected at build
43
+ time — Builders do not register clients by hand.
44
+
45
+ ## Worker: validating the token
46
+
47
+ ```ts
48
+ // src/auth.ts — pattern from the template
49
+ if (env.DEV_AUTH && token === env.DEV_AUTH) {
50
+ return { ok: true, identity: { sub: "dev-user", name: "Local Dev" } };
51
+ }
52
+
53
+ const res = await fetch(`${env.SSO_BASE_URL}/oidc/v1/userinfo`, {
54
+ headers: { authorization: `Bearer ${token}` },
55
+ });
56
+ ```
57
+
58
+ - Missing / invalid token → 401.
59
+ - PPLE ID unreachable / malformed profile → 502 (so the client can tell
60
+ "sign in again" from "try again later").
61
+ - Attach `{ sub, name }` to the request context; keep route handlers thin.
62
+
63
+ ## Local DEV_AUTH only
64
+
65
+ ```env
66
+ # .dev.vars (gitignored) — copy from .dev.vars.example
67
+ DEV_AUTH="dev-token"
68
+ ```
69
+
70
+ Rules:
71
+
72
+ - **Never** set `DEV_AUTH` in production. The platform never provisions it;
73
+ absence means real auth only.
74
+ - The React app's local token must match `DEV_AUTH` exactly (template default:
75
+ `dev-token`).
76
+ - Do not resurrect header-based impersonation (`x-dev-phone`,
77
+ `ALLOW_DEV_AUTH`) — that was the Express-era bypass.
78
+
79
+ ## What not to tell the Builder
80
+
81
+ - Do not claim "tokens only exist inside PPLE Today" as a platform-wide fact
82
+ (Console and CLI also hold Builder tokens).
83
+ - Do not add a custom OAuth redirect login page to the mini-app "so it works
84
+ in Chrome."
85
+ - Do not ask for GitHub or Cloudflare credentials to "get a token."
@@ -0,0 +1,75 @@
1
+ # Project scaffold & local development
2
+
3
+ New Builder Apps start from the golden-path template in
4
+ `pple-platform/template`, copied by `pple init <slug>` after the Console
5
+ creates the app. Do not scaffold Express/Prisma monorepos or copy
6
+ `pple-erp` — that layout is the old path.
7
+
8
+ ## Layout (single package)
9
+
10
+ ```
11
+ my-app/ # folder name matches the App Slug
12
+ ├── package.json # scripts: dev, build, test, deploy, db:migrate:*
13
+ ├── wrangler.jsonc # Worker + D1 + R2 bindings (platform-owned names)
14
+ ├── .dev.vars.example # copy to .dev.vars for local DEV_AUTH
15
+ ├── .pple/app.json # App Link — written by pple init; do not invent
16
+ ├── src/ # Worker (API) — Hono routes under /api/*
17
+ │ ├── index.ts # thin route handlers
18
+ │ ├── auth.ts # PPLE ID userinfo + DEV_AUTH bypass
19
+ │ ├── db.ts # D1 helpers
20
+ │ ├── files.ts # R2 helpers
21
+ │ ├── notify.ts # audience-bound notifications
22
+ │ └── env.ts # Env bindings type
23
+ ├── web/ # React SPA (Vite)
24
+ │ ├── main.tsx # bootstrap → App | NotInApp | error
25
+ │ ├── pple.ts # SDK / DEV_AUTH / api() helpers
26
+ │ ├── App.tsx
27
+ │ ├── NotInApp.tsx
28
+ │ └── app.css # mobile-first layout only
29
+ ├── shared/ # Zod schemas + types used by both sides
30
+ │ └── api.ts
31
+ ├── migrations/ # D1 SQL migrations (0001_*.sql, …)
32
+ └── test/ # smoke tests in the Workers runtime
33
+ ```
34
+
35
+ **`shared/api.ts` is the contract** between Worker and SPA. Change a shape
36
+ there once; both sides import the Zod schema / inferred type.
37
+
38
+ ## Conventions
39
+
40
+ - **One origin.** The Worker serves `/api/*` and the SPA for everything else.
41
+ Frontend calls use relative paths (`/api/notes`) via `api()` / `apiJson()`
42
+ in `web/pple.ts`.
43
+ - **Keep handlers thin.** Put logic in `auth` / `db` / `files` / `notify`.
44
+ - **Validate with Zod** from `shared/api.ts`.
45
+ - **Bind D1 parameters** (`?` + `.bind(...)`) — never string-concatenate SQL.
46
+ - **Add a table** with a new `migrations/000N_*.sql` (never edit an applied
47
+ migration), then `npm run db:migrate:local`.
48
+ - **Mobile-first.** The app runs in a phone-sized WebView.
49
+ - **User-facing strings are Thai** where the template already is; code and
50
+ comments stay English.
51
+
52
+ ## Local development
53
+
54
+ ```bash
55
+ # After Console create-app + pple auth login + pple init <slug>:
56
+ cd <slug>
57
+ npm install
58
+ cp .dev.vars.example .dev.vars # enables DEV_AUTH for plain-browser work
59
+ npm run db:migrate:local
60
+ npm run dev # http://localhost:5173 — SPA + Worker
61
+ ```
62
+
63
+ Everyday commands:
64
+
65
+ | Command | Does |
66
+ | ------- | ---- |
67
+ | `npm run dev` | Full-stack Vite + Worker with hot reload |
68
+ | `npm run typecheck` | Type-check app and Worker |
69
+ | `npm test` | Smoke tests in the Workers runtime |
70
+ | `npm run build` | Deployable bundle |
71
+ | `npm run db:migrate:local` | Apply D1 migrations locally |
72
+ | `pple deploy` | Ship via the platform CLI |
73
+
74
+ In local `vite dev`, `web/pple.ts` uses the `DEV_AUTH` token automatically so
75
+ you never need PPLE Today open. See [pple-id-auth.md](pple-id-auth.md).
@@ -0,0 +1,17 @@
1
+ # Local development configuration. Copy this file to `.dev.vars` (which is
2
+ # gitignored) and adjust. `wrangler dev` and `vite dev` load `.dev.vars`
3
+ # automatically.
4
+
5
+ # Turns on the local auth bypass. When set, any request whose bearer token
6
+ # equals this value is accepted as a fake signed-in user, so you can develop
7
+ # in a plain browser without PPLE Today. The React app uses this token
8
+ # automatically during `vite dev`.
9
+ #
10
+ # NEVER set DEV_AUTH in production — the platform never provisions it, and the
11
+ # Worker treats its absence as "real auth only".
12
+ DEV_AUTH="dev-token"
13
+
14
+ # Optional: exercise the notification helper locally. These are normally
15
+ # injected by the platform at deploy time.
16
+ # NOTIFY_API_BASE="https://today.peoplesparty.or.th"
17
+ # NOTIFY_API_KEY="your-app-notification-key"
@@ -0,0 +1,8 @@
1
+ # Frontend build-time config (Vite exposes VITE_* to the browser bundle).
2
+ # These are the app's PPLE ID OIDC client details, used by the mini-app SDK
3
+ # when running inside PPLE Today. They are public values, safe to ship.
4
+ # The platform injects them at deploy time; copy to `.env` to set locally.
5
+ VITE_OAUTH_URL="https://sso.peoplesparty.or.th"
6
+ VITE_OAUTH_CLIENT_ID=""
7
+ # Defaults to the app's own origin if unset.
8
+ # VITE_OAUTH_REDIRECT_URI=""
@@ -0,0 +1,22 @@
1
+ # CI for a PPLE Platform Builder App. Runs on every push and pull request:
2
+ # type-checks the Worker and the React app, runs the smoke tests in the
3
+ # Workers runtime, and builds the deployable bundle.
4
+ name: CI
5
+
6
+ on:
7
+ push:
8
+ pull_request:
9
+
10
+ jobs:
11
+ check:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 22
18
+ cache: npm
19
+ - run: npm ci
20
+ - run: npm run typecheck
21
+ - run: npm test
22
+ - run: npm run build
@@ -0,0 +1,60 @@
1
+ # Working in this repo
2
+
3
+ This is a **PPLE Platform Builder App** — a mobile-first mini-app that runs
4
+ inside PPLE Today. The person you're helping is a party workforce member who
5
+ may not read stack traces; keep changes simple, explain them plainly, and
6
+ prefer editing the existing demo cards over introducing new patterns.
7
+
8
+ ## Architecture (read once)
9
+
10
+ One Cloudflare Worker serves everything:
11
+
12
+ - **`src/`** — the Worker (API). Hono, TypeScript, Workers runtime. Routes
13
+ live under `/api/*` in `src/index.ts`; keep handlers thin and put logic in
14
+ the helper modules (`auth`, `db`, `files`, `notify`).
15
+ - **`web/`** — the React app (browser). Built by Vite, served for every
16
+ non-`/api` path. Uses the `@pplethai/components` design system.
17
+ - **`shared/`** — types shared by both sides (Zod schemas in `shared/api.ts`).
18
+ When you change an API shape, change it here and both sides update.
19
+ - **`migrations/`** — D1 (SQLite) schema, applied in order.
20
+
21
+ The frontend calls the backend with `api()` / `apiJson()` from `web/pple.ts`,
22
+ which attach the PPLE ID bearer token. Same origin, so paths are relative
23
+ (`/api/notes`).
24
+
25
+ ## How auth works
26
+
27
+ - Inside PPLE Today, the mini-app SDK (`web/pple.ts`) captures the access
28
+ token from the launch URL. The Worker validates it against PPLE ID
29
+ (`src/auth.ts`).
30
+ - In a plain browser, the app shows the NOT_IN_APP screen — there is no
31
+ browser sign-in for mini-apps.
32
+ - Locally, `DEV_AUTH` in `.dev.vars` bypasses PPLE Today. **Never** set
33
+ `DEV_AUTH` in production; the platform never provisions it.
34
+
35
+ ## Conventions
36
+
37
+ - **TypeScript is strict.** Run `npm run typecheck` after changes.
38
+ - **Validate input with the Zod schemas** in `shared/api.ts`.
39
+ - **Always bind D1 query parameters** (`?` + `.bind(...)`) — never build SQL
40
+ with string concatenation.
41
+ - **Notifications are content only** (title/body), plus an optional path-only
42
+ `linkPath` into this app. Never send phone numbers or user ids; the platform
43
+ resolves the audience. See `src/notify.ts`.
44
+ - **Mobile-first.** The app runs in a phone-sized WebView. Use the design
45
+ system's components and the layout classes in `web/app.css`.
46
+ - **Add a DB table** by writing a new `migrations/000N_*.sql` (never edit an
47
+ applied migration) and running `npm run db:migrate:local`.
48
+
49
+ ## Before you finish
50
+
51
+ Run these and make sure they pass:
52
+
53
+ ```bash
54
+ npm run typecheck
55
+ npm test
56
+ npm run build
57
+ ```
58
+
59
+ `npm test` runs the smoke tests inside the real Workers runtime. If you add a
60
+ capability, add a smoke test for it in `test/`.
@@ -0,0 +1,60 @@
1
+ # PPLE Platform — Golden-path template
2
+
3
+ The starting point for a **Builder App** on the PPLE Platform. It's a
4
+ mobile-first mini-app that runs inside PPLE Today, built on the platform's
5
+ managed Cloudflare stack, wired to the People's Party identity, data, file,
6
+ and notification services out of the box.
7
+
8
+ You don't need to understand Cloudflare, OAuth, or SQL to use it — describe
9
+ what you want to your AI coding agent and edit the demo cards. See
10
+ [CLAUDE.md](CLAUDE.md) for how the agent should work in this repo.
11
+
12
+ ## What's inside
13
+
14
+ | Capability | Where | Demo |
15
+ | ---------- | ----- | ---- |
16
+ | **API** | Hono Worker, `src/index.ts` | routes under `/api/*` |
17
+ | **Identity** | `src/auth.ts` + `web/pple.ts` | validates PPLE ID tokens; NOT_IN_APP screen; dev bypass |
18
+ | **Database** | D1, `src/db.ts` + `migrations/` | the Notes card |
19
+ | **Files** | R2, `src/files.ts` | the Files card |
20
+ | **Notifications** | `src/notify.ts` | the Notify card (audience-bound; optional `linkPath`) |
21
+ | **Design system** | `@pplethai/components` | every screen |
22
+
23
+ One Worker serves the API under `/api/*` and the React app (`web/`) for
24
+ everything else — same origin, no CORS.
25
+
26
+ ## Getting started
27
+
28
+ ```bash
29
+ npm install
30
+ cp .dev.vars.example .dev.vars # local auth bypass
31
+ npm run db:migrate:local # set up the local database
32
+ npm run dev # http://localhost:5173
33
+ ```
34
+
35
+ In local dev you're signed in automatically as a fake user (the `DEV_AUTH`
36
+ bypass), so you can build without PPLE Today. Opened in a plain browser after
37
+ deploy, the app shows a friendly "open in PPLE Today" screen; opened inside
38
+ PPLE Today, it signs the real user in via the mini-app SDK.
39
+
40
+ ## Everyday commands
41
+
42
+ | Command | Does |
43
+ | ------- | ---- |
44
+ | `npm run dev` | Full-stack dev server (React + Worker) with hot reload |
45
+ | `npm run typecheck` | Type-check the app and the Worker |
46
+ | `npm test` | Run the smoke tests in the Workers runtime |
47
+ | `npm run build` | Build the deployable bundle |
48
+ | `npm run db:migrate:local` | Apply migrations to the local database |
49
+ | `pple deploy` | Ship it (the platform CLI; wraps `npm run deploy`) |
50
+
51
+ ## Making it yours
52
+
53
+ - **Add a table:** create `migrations/0002_*.sql`, run `npm run db:migrate:local`,
54
+ add queries in `src/db.ts`.
55
+ - **Add an API route:** add it under `/api` in `src/index.ts`.
56
+ - **Change the UI:** edit `web/App.tsx` using `@pplethai/components`.
57
+ - **Send notifications:** the platform provisions `NOTIFY_API_KEY`; the helper
58
+ in `src/notify.ts` already speaks the audience-bound contract.
59
+
60
+ The demo cards (Notes / Files / Notify) are examples — replace them.
@@ -0,0 +1,6 @@
1
+ node_modules/
2
+ dist/
3
+ .wrangler/
4
+ .dev.vars
5
+ *.log
6
+ .claude/skills/pple-platform/
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="th">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <!-- Mobile-first: fit the notch/safe areas of the PPLE Today WebView. -->
6
+ <meta
7
+ name="viewport"
8
+ content="width=device-width, initial-scale=1, viewport-fit=cover"
9
+ />
10
+ <title>PPLE App</title>
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ <script type="module" src="/web/main.tsx"></script>
15
+ </body>
16
+ </html>
@@ -0,0 +1,9 @@
1
+ -- The example table behind the D1 read/write demo. Replace it with your own
2
+ -- schema: add a new file (0002_add_something.sql, …) and apply it with
3
+ -- `npm run db:migrate:local` (local) or `npm run db:migrate:remote` (deployed).
4
+ CREATE TABLE notes (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ body TEXT NOT NULL,
7
+ -- ISO-8601 UTC (…T…Z) so the timestamp is unambiguous everywhere it's read.
8
+ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now'))
9
+ );