spaps 0.7.2 → 0.7.4

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 (49) hide show
  1. package/AI_TOOLS.json +10 -11
  2. package/README.md +267 -110
  3. package/assets/local-runtime/Dockerfile +28 -0
  4. package/assets/local-runtime/alembic/env.py +101 -0
  5. package/assets/local-runtime/alembic/path_bootstrap.py +71 -0
  6. package/assets/local-runtime/alembic/versions/000000000001_baseline_consolidated_schema.py +1076 -0
  7. package/assets/local-runtime/alembic/versions/000000000002_fix_column_types_to_match_prod.py +83 -0
  8. package/assets/local-runtime/alembic/versions/000000000003_fix_email_template_key_uniqueness.py +49 -0
  9. package/assets/local-runtime/alembic/versions/000000000004_add_hold_duration_minutes_to_dayrate_config.py +30 -0
  10. package/assets/local-runtime/alembic/versions/000000000005_resource_scoped_entitlements.py +77 -0
  11. package/assets/local-runtime/alembic/versions/000000000006_cfo_rbac_add_is_admin.py +37 -0
  12. package/assets/local-runtime/alembic/versions/000000000007_agent_approvals.py +158 -0
  13. package/assets/local-runtime/alembic/versions/000000000008_add_company_id_to_cfo_connections.py +35 -0
  14. package/assets/local-runtime/alembic/versions/000000000009_tx_signing.py +62 -0
  15. package/assets/local-runtime/alembic/versions/000000000010_affiliate_referrals.py +235 -0
  16. package/assets/local-runtime/alembic/versions/000000000011_checkin_call_booking.py +137 -0
  17. package/assets/local-runtime/alembic/versions/000000000012_subscription_application_scoping.py +55 -0
  18. package/assets/local-runtime/alembic/versions/000000000013_refresh_token_anomaly_context.py +61 -0
  19. package/assets/local-runtime/alembic/versions/000000000014_buildooor_dayrate_hire_schedule.py +39 -0
  20. package/assets/local-runtime/alembic/versions/000000000015_support_telemetry_platform.py +112 -0
  21. package/assets/local-runtime/alembic/versions/000000000016_issue_reporting_platform.py +54 -0
  22. package/assets/local-runtime/alembic/versions/000000000017_issue_reporting_platform_import_tracking.py +44 -0
  23. package/assets/local-runtime/alembic/versions/000000000018_authorization_policy_engine.py +76 -0
  24. package/assets/local-runtime/alembic.ini +47 -0
  25. package/assets/local-runtime/docker-compose.yml +61 -0
  26. package/assets/local-runtime/manifest.json +8 -0
  27. package/assets/local-runtime/scripts/container-entrypoint.sh +13 -0
  28. package/assets/local-runtime/scripts/fetch-prod-db.sh +112 -0
  29. package/assets/local-runtime/scripts/run-migrations.sh +96 -0
  30. package/package.json +5 -4
  31. package/src/ai-helper.js +176 -234
  32. package/src/ai-tool-spec.js +52 -20
  33. package/src/auth/api-key.js +119 -0
  34. package/src/auth/client-id.js +136 -0
  35. package/src/auth/client.js +169 -0
  36. package/src/auth/credentials.js +110 -0
  37. package/src/auth/device-flow.js +159 -0
  38. package/src/auth/env.js +57 -0
  39. package/src/auth/handlers.js +462 -0
  40. package/src/auth/http.js +74 -0
  41. package/src/cli-dispatcher.js +155 -24
  42. package/src/docs-system.js +7 -7
  43. package/src/error-handler.js +42 -0
  44. package/src/fixture-kernel.js +1143 -0
  45. package/src/handlers.js +252 -15
  46. package/src/help-system.js +3 -1
  47. package/src/local-runtime.js +258 -0
  48. package/src/local-server.js +597 -199
  49. package/src/project-scaffolder.js +441 -0
package/AI_TOOLS.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "spaps",
3
- "version": "0.1.0",
4
- "description": "Auth + payments via SPAPS (local by default). Start with: npx spaps local",
3
+ "version": "0.7.4",
4
+ "description": "Auth + payments via SPAPS. Runtime auth requirements come from /health/local-mode or `npx spaps tools --json`.",
5
5
  "base_url": "http://localhost:3301",
6
6
  "auth": {
7
- "local_mode": true,
8
- "production": {
9
- "header": "X-API-Key",
10
- "env": "SPAPS_API_KEY"
11
- }
7
+ "local_mode": null,
8
+ "mode_source": "/health/local-mode",
9
+ "api_key_required_when_local_mode_disabled": true,
10
+ "header": "X-API-Key",
11
+ "env": "SPAPS_API_KEY"
12
12
  },
13
13
  "tools": [
14
14
  {
15
15
  "name": "login",
16
- "description": "Login with email/password. Local mode accepts any values.",
16
+ "description": "Login with email/password. Send X-API-Key when local mode is disabled.",
17
17
  "method": "POST",
18
18
  "path": "/api/auth/login",
19
19
  "parameters": {
@@ -71,7 +71,7 @@
71
71
  },
72
72
  {
73
73
  "name": "list_products",
74
- "description": "List products (Stripe-backed or local).",
74
+ "description": "List products exposed by the SPAPS server.",
75
75
  "method": "GET",
76
76
  "path": "/api/stripe/products",
77
77
  "parameters": {
@@ -84,7 +84,7 @@
84
84
  },
85
85
  {
86
86
  "name": "request_magic_link",
87
- "description": "Send a magic link for passwordless login (simulated locally).",
87
+ "description": "Send a magic link for passwordless login.",
88
88
  "method": "POST",
89
89
  "path": "/api/auth/magic-link",
90
90
  "parameters": {
@@ -111,4 +111,3 @@
111
111
  }
112
112
  ]
113
113
  }
114
-
package/README.md CHANGED
@@ -1,223 +1,380 @@
1
1
  # spaps
2
2
 
3
- CLI and middleware package for local SPAPS workflows.
3
+ Test SPAPS auth in a new app before you build auth backend infrastructure.
4
+
5
+ `spaps` is the shortest path from "I have a new frontend" to "this app can hit authenticated SPAPS routes locally." It wraps the local SPAPS server, tells you what auth mode the server is actually in, scaffolds starter code, and provisions a real local application when the server requires one.
4
6
 
5
7
  ## TL;DR
6
8
 
7
- **The Problem**: Local auth and payment development is slow when every app has to bootstrap a backend, wire environment files, and remember the right health and docs commands.
9
+ ### The problem
10
+
11
+ You want to wire auth into a new app, but you do not want to design and ship your own auth backend first.
12
+
13
+ ### The shortcut
8
14
 
9
- **The Solution**: `spaps` gives you a small CLI for local-server workflows plus exported admin middleware helpers for Node apps that integrate with SPAPS.
15
+ Run SPAPS locally, inspect the current auth mode, then either use local mode directly or provision a real local app with one command. The CLI tells you which mode the server is actually in, so you do not have to guess.
10
16
 
11
17
  ### Why Use `spaps`?
12
18
 
13
- | Feature | What It Does |
19
+ | Need | `spaps` gives you |
20
+ | --- | --- |
21
+ | "Can I test auth in this app today?" | `spaps local`, `status`, and `quickstart` |
22
+ | "Do I need a real app key or not?" | Runtime truth from `/health/local-mode` |
23
+ | "Can you scaffold the wiring for me?" | `spaps create <name> --template ...` |
24
+ | "I want a working local app, not a fake contract" | Self-service provisioning when `SELF_SERVICE_PASSWORD` is available |
25
+ | "If provisioning fails, tell me exactly why" | Explicit `scaffold_only` fallback and warnings |
26
+ | "I want persistent local personas, roles, and entitlements in this repo" | `spaps fixtures ...` and a repo-local `.spaps/` kernel |
27
+
28
+ ## No Backend Yet? Start Here
29
+
30
+ ```bash
31
+ npx spaps local
32
+ npx spaps quickstart --json | jq '.auth'
33
+ SELF_SERVICE_PASSWORD=your-password npx spaps create demo-app --template react
34
+ cd demo-app
35
+ npm install
36
+ ```
37
+
38
+ What happens next depends on the server mode:
39
+
40
+ | Server state | What `spaps` does | What you do |
41
+ | --- | --- | --- |
42
+ | `local_mode_active: true` | Exposes local-mode hints and test personas | Use the starter against `localhost`; no app key provisioning required |
43
+ | `local_mode_active: false` and `SELF_SERVICE_PASSWORD` is set | Provisions a real local SPAPS app and writes the working key into `.env.local` | Start wiring auth flows in your app immediately |
44
+ | Server unreachable or no self-service password | Scaffolds files only and tells you why provisioning was skipped | Bring the server up or rerun with `SELF_SERVICE_PASSWORD` later |
45
+
46
+ This package targets `Node.js >=22`.
47
+
48
+ ## Local Runtime Modes
49
+
50
+ `spaps local` now has two runtime paths:
51
+
52
+ | Runtime path | When it is used | What it does |
53
+ | --- | --- | --- |
54
+ | `repo` | You are running from the `sweet-potato` checkout | Uses the repo's Docker Compose stack and source-mounted assets |
55
+ | `bundle` | You installed `spaps` from npm elsewhere | Uses bundled Docker assets plus the published `spaps-server-quickstart` package |
56
+
57
+ The default is `auto`: use repo assets when they exist, otherwise fall back to the bundled runtime. The bundled runtime defaults to `SPAPS_LOCAL_MODE=true`, so RBAC fixtures and test personas work out of the box.
58
+
59
+ ## Local Data Sources
60
+
61
+ `spaps local` separates runtime selection from base data selection:
62
+
63
+ | Data source | What it does |
14
64
  | --- | --- |
15
- | Local server workflow | Start, stop, inspect, and diagnose a local SPAPS server from the CLI |
16
- | Project bootstrap | Create a starter `.env.local` for SPAPS-aware projects |
17
- | AI tooling output | Emit the local OpenAI-style tool spec with `spaps tools` |
18
- | Middleware exports | Reuse admin-permission helpers from the package API |
65
+ | `empty` | Boot an empty local DB and let migrations create schema |
66
+ | `prod-cache` | Restore the cached SPAPS production dump before the API starts, then reuse it until the cached dump changes |
67
+ | `prod-fresh` | Force a fresh production dump fetch, restore it, then boot the API |
68
+ | `--from-backup <path>` | Restore a specific `.sql.gz` dump file before boot |
69
+
70
+ Restore order is always: restore base dump first, then let the API container run migrations to head on top of it.
19
71
 
20
- ## Installation
72
+ ## Install
21
73
 
22
- ### Run Without Installing
74
+ Run it without installing:
23
75
 
24
76
  ```bash
25
77
  npx spaps help
26
78
  ```
27
79
 
28
- ### Global Install
80
+ Install globally:
29
81
 
30
82
  ```bash
31
83
  npm install -g spaps
32
84
  ```
33
85
 
34
- ### Project Dependency
86
+ Add it to a project:
35
87
 
36
88
  ```bash
37
89
  npm install spaps
38
90
  ```
39
91
 
40
- ## Quick Example
92
+ ## Quick Start
93
+
94
+ | Step | Command | Why |
95
+ | --- | --- |
96
+ | 1 | `npx spaps local` | Start the local SPAPS stack |
97
+ | 1b | `npx spaps local --data-source prod-cache` | Start the local stack with a cached prod-backed base DB |
98
+ | 2 | `npx spaps status --json` | Confirm the server is reachable |
99
+ | 3 | `npx spaps quickstart --json` | See whether local mode is active or a real app key is required |
100
+ | 4 | `npx spaps create my-app --template react` | Scaffold a starter and, when possible, provision a real local app |
101
+ | 5 | `npx spaps tools --json` | Export the current AI tool contract for agents or tests |
102
+ | 6 | `npx spaps fixtures apply` | Materialize repo-local personas into Playwright/browser artifacts |
103
+
104
+ ## CLI Surface
105
+
106
+ | Command | Purpose | Common flags |
107
+ | --- | --- | --- |
108
+ | `spaps local [stop]` | Start or stop the local server workflow | `--port`, `--runtime-dir`, `--runtime-source`, `--data-source`, `--detach`, `--fresh`, `--from-backup`, `--open`, `--json` |
109
+ | `spaps status` | Check whether the local server is running | `--port`, `--json` |
110
+ | `spaps quickstart` | Print quick-start instructions | `--port`, `--json` |
111
+ | `spaps init` | Create a starter `.env.local` | `--json` |
112
+ | `spaps create <name>` | Scaffold a SPAPS starter project directory and try local provisioning | `--template`, `--dir`, `--port`, `--force`, `--json` |
113
+ | `spaps fixtures <subcommand>` | Manage repo-local `.spaps` auth fixtures | `--dir`, `--port`, `--base-url`, `--persona`, `--format`, `--force`, `--json` |
114
+ | `spaps docs` | Browse or search bundled docs | `--interactive`, `--search`, `--json` |
115
+ | `spaps tools` | Emit the AI tool spec | `--port`, `--format`, `--json` |
116
+ | `spaps doctor` | Diagnose local environment problems | `--port`, `--stripe`, `--json` |
117
+
118
+ Example command usage:
41
119
 
42
120
  ```bash
43
- # Start the local server
44
- npx spaps local
121
+ spaps local --port 3400 --detach
122
+ spaps local --runtime-source bundle --runtime-dir ./.skillbox/spaps-local
123
+ spaps local --runtime-source repo --data-source prod-cache
124
+ spaps local --runtime-source repo --fresh --data-source prod-fresh
125
+ spaps local --from-backup ~/.cache/spaps/db/prod.sql.gz
126
+ spaps status --json
127
+ spaps create my-app --template react
128
+ spaps login --json
129
+ spaps fixtures apply --base-url http://localhost:5173
130
+ spaps fixtures storage-state --persona admin
131
+ spaps docs --search secure-messages
132
+ spaps doctor --stripe mock
133
+ spaps local stop
134
+ ```
45
135
 
46
- # Check status
47
- npx spaps status
136
+ ## CLI Auth
48
137
 
49
- # Initialize .env.local in the current project
50
- npx spaps init
138
+ `spaps login` uses the active FastAPI device-flow contract:
51
139
 
52
- # Emit the local tool spec as JSON
53
- npx spaps tools --json
54
- ```
140
+ - device authorization at `/api/cli/device/*`
141
+ - authenticated follow-up calls at `/api/auth/*`
142
+ - standard SPAPS response envelopes
143
+
144
+ The CLI no longer assumes a built-in `spaps-cli` application slug. Resolve the client id in this order:
55
145
 
56
- ## CLI Commands
146
+ - `--client-id <app-slug>`
147
+ - `SPAPS_CLI_CLIENT_ID`
148
+ - repo-local `spaps.app.json`
149
+ - repo-local `.spaps/app.json`
150
+ - `/health/local-mode` test application metadata
57
151
 
58
- ### `spaps local [subcommand]`
152
+ Authenticated follow-up calls such as `whoami`, `logout`, and token refresh still need a normal SPAPS app key when the server is not in local mode. The CLI resolves that key from:
59
153
 
60
- Start or stop the local SPAPS server.
154
+ - `SPAPS_API_KEY`
155
+ - `NEXT_PUBLIC_SPAPS_API_KEY`
156
+ - `VITE_SPAPS_API_KEY`
157
+ - repo-local `.env.local`
158
+ - repo-local `.env`
159
+
160
+ Examples:
61
161
 
62
162
  ```bash
63
- spaps local
64
- spaps local --port 3400
65
- spaps local --detach
66
- spaps local --open
67
- spaps local stop
163
+ npx spaps login --client-id my-app
164
+ SPAPS_CLI_CLIENT_ID=my-app npx spaps login
165
+ VITE_SPAPS_API_KEY=spaps_pub_demo npx spaps whoami --json
68
166
  ```
69
167
 
70
- Supported flags:
168
+ Still reserved and not finished:
71
169
 
72
- - `--port <port>`
73
- - `--detach`
74
- - `--fresh`
75
- - `--from-backup <path>`
76
- - `--open`
77
- - `--json`
170
+ - `spaps types`
78
171
 
79
- ### `spaps status`
172
+ ## Create A Starter Project
80
173
 
81
- ```bash
82
- spaps status
83
- spaps status --port 3400
84
- spaps status --json
85
- ```
174
+ `spaps create` ships a local-first starter kit rather than a full framework generator. It creates a new directory with:
86
175
 
87
- ### `spaps quickstart`
176
+ - `spaps.app.json` for the machine-readable SPAPS app contract
177
+ - `.env.local` pointing at local SPAPS and, when available, a working template-appropriate key
178
+ - `package.json` with `spaps-sdk`
179
+ - a small template-specific integration starter
88
180
 
89
- ```bash
90
- spaps quickstart
91
- spaps quickstart --port 3400
92
- spaps quickstart --json
93
- ```
181
+ When the local server is reachable and `SELF_SERVICE_PASSWORD` is set, `create` also provisions a real local SPAPS application. When that is not possible, it falls back to scaffold-only mode and tells you why.
94
182
 
95
- ### `spaps init`
183
+ Supported templates:
96
184
 
97
- ```bash
98
- spaps init
99
- spaps init --json
100
- ```
185
+ - `nextjs`
186
+ - `react`
187
+ - `node`
188
+ - `vanilla`
101
189
 
102
- ### `spaps docs`
190
+ Examples:
103
191
 
104
192
  ```bash
105
- spaps docs
106
- spaps docs --interactive
107
- spaps docs --search secure-messages
108
- spaps docs --json
193
+ npx spaps create my-app --template react
194
+ npx spaps create my-api --template node --dir ./services/my-api
195
+ SELF_SERVICE_PASSWORD=your-password npx spaps create my-app --template react
109
196
  ```
110
197
 
111
- ### `spaps tools`
198
+ The generated starter is template-aware:
112
199
 
113
- ```bash
114
- spaps tools
115
- spaps tools --port 3400
116
- spaps tools --format openai
117
- spaps tools --json
118
- ```
200
+ - browser templates write a publishable key when provisioning succeeds
201
+ - server templates write a server key when provisioning succeeds
202
+ - `spaps.app.json` records provisioning status and application id, but never stores raw keys
203
+
204
+ ## Repo-Local Fixtures
205
+
206
+ Use `.spaps/` when you want persistent local personas for clicking around, Playwright, or app-level auth tests without inventing ad hoc storage keys in each repo.
119
207
 
120
- ### `spaps doctor`
208
+ `spaps fixtures init` creates:
209
+
210
+ - `.spaps/app.json` for runtime and browser target settings
211
+ - `.spaps/users.json` for personas and profile data
212
+ - `.spaps/roles.json` for RBAC grants
213
+ - `.spaps/entitlements.json` for entitlement grants
214
+ - `.spaps/browser/` for generated storage-state and header artifacts
215
+
216
+ Then run:
121
217
 
122
218
  ```bash
123
- spaps doctor
124
- spaps doctor --port 3400
125
- spaps doctor --stripe mock
126
- spaps doctor --json
219
+ npx spaps fixtures apply --base-url http://localhost:5173
220
+ npx spaps fixtures storage-state --persona admin
127
221
  ```
128
222
 
129
- ### Placeholder Commands
223
+ What `apply` emits:
130
224
 
131
- The CLI currently includes placeholders for:
225
+ - `browser/<persona>.storage-state.json` for Playwright `storageState`
226
+ - `browser/<persona>.headers.json` for `extraHTTPHeaders`
227
+ - `browser/<persona>.context.json` with merged persona/runtime metadata
228
+ - `public/spaps-dev-auth.js` for frontend-only auth/RBAC dev mode
132
229
 
133
- - `spaps create <name>`
134
- - `spaps types`
230
+ This does not try to import browser password-manager state. It writes deterministic, repo-local test artifacts instead.
135
231
 
136
- Treat them as reserved surfaces rather than stable workflows.
232
+ If you want a human to launch the frontend and click around without a mock backend, include the generated bridge before your app boots:
137
233
 
138
- ## Middleware Example
234
+ ```html
235
+ <script src="/spaps-dev-auth.js"></script>
236
+ ```
139
237
 
140
- The package exports admin middleware and helper utilities from its main module:
238
+ That bridge does three things:
141
239
 
142
- ```javascript
143
- const express = require('express');
144
- const { requireAdmin, requirePermission } = require('spaps');
240
+ - seeds SDK-compatible localStorage keys like `sweet_potato_user` and `sweet_potato_access_token`
241
+ - intercepts `/api/auth/user`, `/api/auth/login`, `/api/auth/logout`, `/api/entitlements`, and `/api/entitlements/check`
242
+ - renders a tiny persona switcher so you can flip between `user`, `admin`, and `premium`
145
243
 
146
- const app = express();
244
+ ## Middleware Example
147
245
 
148
- app.get('/admin', requireAdmin(), (_req, res) => {
149
- res.json({ ok: true });
150
- });
246
+ The main module exports admin and permission helpers for Express-style apps.
151
247
 
152
- app.post('/admin/products', requirePermission('manage_products'), (_req, res) => {
153
- res.json({ created: true });
154
- });
248
+ ```js
249
+ const express = require("express");
250
+ const { requireAdmin, requirePermission } = require("spaps");
251
+
252
+ const app = express();
253
+ const customAdmins = ["admin@example.com"];
254
+
255
+ app.get(
256
+ "/admin",
257
+ requireAdmin({ customAdmins }),
258
+ (_req, res) => {
259
+ res.json({ ok: true });
260
+ },
261
+ );
262
+
263
+ app.post(
264
+ "/admin/products",
265
+ requirePermission("manage_products", { customAdmins }),
266
+ (_req, res) => {
267
+ res.json({ created: true });
268
+ },
269
+ );
155
270
  ```
156
271
 
157
272
  ## What `spaps init` Writes
158
273
 
159
- `spaps init` creates a `.env.local` with a local API URL starter and leaves existing files alone if they are already present.
274
+ `spaps init` creates a `.env.local` file with a local API URL starter and leaves an existing file alone if one is already present.
160
275
 
161
276
  ## Troubleshooting
162
277
 
163
278
  ### Port already in use
164
279
 
165
- Start on a different port:
280
+ Start on another port:
166
281
 
167
282
  ```bash
168
283
  npx spaps local --port 3400
169
284
  ```
170
285
 
171
- ### Need machine-readable output
286
+ ### I need deterministic portable runtime files
287
+
288
+ Pin the bundled runtime directory explicitly:
289
+
290
+ ```bash
291
+ npx spaps local --runtime-source bundle --runtime-dir ./.skillbox/spaps-local
292
+ ```
293
+
294
+ This is the recommended path for managed environments such as skillbox.
295
+
296
+ ### I need machine-readable output
172
297
 
173
- Use `--json` on supported commands such as `local`, `status`, `quickstart`, `init`, `docs`, `tools`, and `doctor`.
298
+ Use `--json` on commands that support it, including `local`, `status`, `quickstart`, `init`, `fixtures`, `docs`, `tools`, and `doctor`.
174
299
 
175
- ### Local server is not responding
300
+ ### The local server is not responding
176
301
 
177
- Check the current status first:
302
+ Check current status first:
178
303
 
179
304
  ```bash
180
305
  npx spaps status
181
306
  ```
182
307
 
183
- Then re-run the server with a clean start if needed:
308
+ If needed, restart with a clean boot:
184
309
 
185
310
  ```bash
186
311
  npx spaps local --fresh
187
312
  ```
188
313
 
314
+ If you want the old "db=fresh from prod" behavior through the portable CLI:
315
+
316
+ ```bash
317
+ npx spaps local --fresh --data-source prod-fresh
318
+ ```
319
+
320
+ If you want to keep a cached prod-backed base DB around for day-to-day work:
321
+
322
+ ```bash
323
+ npx spaps local --data-source prod-cache
324
+ ```
325
+
326
+ ### `spaps create` only scaffolded files
327
+
328
+ That means one of two things:
329
+
330
+ - the local server was unreachable
331
+ - the server is not in local mode and `SELF_SERVICE_PASSWORD` was missing or invalid
332
+
333
+ Check the current mode first:
334
+
335
+ ```bash
336
+ npx spaps quickstart --json
337
+ ```
338
+
339
+ If the server requires provisioning, rerun with:
340
+
341
+ ```bash
342
+ SELF_SERVICE_PASSWORD=your-password npx spaps create my-app --template react --force
343
+ ```
344
+
189
345
  ## Limitations
190
346
 
191
- - The README only documents commands and flags that are currently wired in the CLI dispatcher.
192
- - `create` and `types` are present as placeholders and should not be treated as finished product surfaces.
193
- - The package is primarily for local workflows, not full production deployment automation.
347
+ - The CLI is aimed at local workflows. It is not a full deployment or hosting tool.
348
+ - `create` does not run `create-next-app`, Vite, or Express generators for you; it scaffolds the SPAPS integration layer.
349
+ - `types` is still a placeholder today.
350
+ - The middleware ships a legacy default admin configuration in code; public-facing apps should pass explicit `customAdmins` instead of relying on that default.
194
351
 
195
352
  ## FAQ
196
353
 
197
- ### Does this package expose the TypeScript SDK?
354
+ ### What is this package actually for?
355
+
356
+ Use it when you want to prove auth wiring in a new app before you build backend auth infrastructure yourself.
357
+
358
+ ### Does this package include the TypeScript SDK?
198
359
 
199
- No. The SDK is the separate `spaps-sdk` package.
360
+ No. The SDK is published separately as `spaps-sdk`.
200
361
 
201
- ### Can I use the CLI without installing globally?
362
+ ### Can I use the CLI without a global install?
202
363
 
203
364
  Yes. Use `npx spaps ...`.
204
365
 
205
- ### Does `spaps init` overwrite my existing env file?
366
+ ### Does `spaps init` overwrite an existing env file?
206
367
 
207
368
  No. It skips `.env.local` if the file already exists.
208
369
 
209
370
  ### What does `spaps tools` output?
210
371
 
211
- An OpenAI-style tool spec for the local SPAPS surface.
212
-
213
- ### Is the admin middleware available as an import?
214
-
215
- Yes. The package exports the admin helpers from its main module and via the `./middleware` export path.
372
+ An OpenAI-style tool spec for the local SPAPS surface. The auth section is derived from `/health/local-mode` when the local server is reachable.
216
373
 
217
- ## About Contributions
374
+ ### Is the middleware available from a subpath?
218
375
 
219
- *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other "stakeholders," which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.
376
+ Yes. You can import from the main module or `spaps/middleware`.
220
377
 
221
378
  ## License
222
379
 
223
- UNLICENSED
380
+ MIT
@@ -0,0 +1,28 @@
1
+ # Portable SPAPS local runtime image.
2
+ # Built from bundled assets shipped inside the npm package.
3
+
4
+ FROM python:3.12-slim AS base
5
+
6
+ WORKDIR /app
7
+
8
+ RUN apt-get update && \
9
+ apt-get install -y --no-install-recommends \
10
+ libpq-dev \
11
+ gcc \
12
+ curl \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ ARG SPAPS_SERVER_QUICKSTART_VERSION=0.5.0
16
+ RUN pip install --no-cache-dir "spaps-server-quickstart==${SPAPS_SERVER_QUICKSTART_VERSION}"
17
+
18
+ COPY alembic.ini /app/alembic.ini
19
+ COPY alembic/ /app/alembic/
20
+ COPY scripts/ /app/scripts/
21
+
22
+ ENV APP_ROOT=/app
23
+ ENV PYTHONUNBUFFERED=1
24
+
25
+ EXPOSE 8000
26
+
27
+ ENTRYPOINT ["/app/scripts/container-entrypoint.sh"]
28
+ CMD ["uvicorn", "spaps_server_quickstart.spaps_app:app", "--host", "0.0.0.0", "--port", "8000"]
@@ -0,0 +1,101 @@
1
+ """
2
+ Alembic environment for SPAPS.
3
+
4
+ Imports all domain models from spaps_server_quickstart.domains so that
5
+ ``Base.metadata`` contains every table. Alembic uses this metadata for
6
+ autogenerate and schema diffing.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import sys
13
+ from logging.config import fileConfig
14
+
15
+ from alembic import context
16
+ from sqlalchemy import engine_from_config, pool
17
+
18
+ # Ensure Alembic helper modules are importable, then prefer the active
19
+ # quickstart source tree (for local Docker this should be the live /app/src bind
20
+ # mount, not the packaged fallback copied into the image).
21
+ sys.path.insert(0, os.path.dirname(__file__))
22
+
23
+ from path_bootstrap import ensure_quickstart_src_path # noqa: E402
24
+
25
+ ensure_quickstart_src_path(__file__, sys.path)
26
+
27
+ from spaps_server_quickstart.domains._db.base import Base # noqa: E402
28
+
29
+ # Phase 1+: import domain models here so Base.metadata picks them up
30
+ # from spaps_server_quickstart.domains.users.models import User # noqa: E402, F401
31
+ # from spaps_server_quickstart.domains.auth.models import ... # noqa: E402, F401
32
+ from spaps_server_quickstart.domains.approval_authz_prod_hardening.models import GovernanceAuthzAuditEvent # noqa: E402, F401
33
+ from spaps_server_quickstart.domains.support_telemetry_platform.models import SupportCase, SupportCaseEvent # noqa: E402, F401
34
+ from spaps_server_quickstart.domains.issue_reporting_platform.models import IssueReport # noqa: E402, F401
35
+
36
+ config = context.config
37
+
38
+ if config.config_file_name is not None:
39
+ fileConfig(config.config_file_name)
40
+
41
+ target_metadata = Base.metadata
42
+
43
+
44
+ def get_database_url() -> str:
45
+ """Resolve the database URL from environment or settings."""
46
+ url = os.environ.get("DATABASE_URL", "")
47
+ if url:
48
+ # Alembic runs synchronously — convert asyncpg to psycopg
49
+ if "asyncpg" in url:
50
+ url = url.replace("postgresql+asyncpg", "postgresql+psycopg")
51
+ return url
52
+
53
+ # Fallback: try loading from settings
54
+ try:
55
+ from spaps_server_quickstart.spaps_settings import get_spaps_settings
56
+
57
+ settings = get_spaps_settings()
58
+ return settings.sync_database_url
59
+ except Exception:
60
+ return "postgresql+psycopg://postgres:postgres@localhost:5432/spaps"
61
+
62
+
63
+ def run_migrations_offline() -> None:
64
+ """Run migrations in 'offline' mode (generate SQL without DB connection)."""
65
+ url = get_database_url()
66
+ context.configure(
67
+ url=url,
68
+ target_metadata=target_metadata,
69
+ literal_binds=True,
70
+ dialect_opts={"paramstyle": "named"},
71
+ )
72
+
73
+ with context.begin_transaction():
74
+ context.run_migrations()
75
+
76
+
77
+ def run_migrations_online() -> None:
78
+ """Run migrations with a live database connection."""
79
+ alembic_config = config.get_section(config.config_ini_section, {})
80
+ alembic_config["sqlalchemy.url"] = get_database_url()
81
+
82
+ connectable = engine_from_config(
83
+ alembic_config,
84
+ prefix="sqlalchemy.",
85
+ poolclass=pool.NullPool,
86
+ )
87
+
88
+ with connectable.connect() as connection:
89
+ context.configure(
90
+ connection=connection,
91
+ target_metadata=target_metadata,
92
+ )
93
+
94
+ with context.begin_transaction():
95
+ context.run_migrations()
96
+
97
+
98
+ if context.is_offline_mode():
99
+ run_migrations_offline()
100
+ else:
101
+ run_migrations_online()