primitive-admin 1.1.0-alpha.3 → 1.1.0-alpha.30
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.
- package/README.md +125 -21
- package/assets/skill/skills/primitive-platform/SKILL.md +167 -0
- package/dist/bin/primitive.js +192 -15
- package/dist/bin/primitive.js.map +1 -1
- package/dist/src/commands/admins.js +107 -0
- package/dist/src/commands/admins.js.map +1 -1
- package/dist/src/commands/analytics.js +464 -55
- package/dist/src/commands/analytics.js.map +1 -1
- package/dist/src/commands/apps.js +14 -0
- package/dist/src/commands/apps.js.map +1 -1
- package/dist/src/commands/auth.js +114 -4
- package/dist/src/commands/auth.js.map +1 -1
- package/dist/src/commands/blob-buckets.js +354 -0
- package/dist/src/commands/blob-buckets.js.map +1 -0
- package/dist/src/commands/collection-type-configs.js +178 -0
- package/dist/src/commands/collection-type-configs.js.map +1 -0
- package/dist/src/commands/collections.js +526 -0
- package/dist/src/commands/collections.js.map +1 -0
- package/dist/src/commands/cron-triggers.js +364 -0
- package/dist/src/commands/cron-triggers.js.map +1 -0
- package/dist/src/commands/database-types.js +462 -0
- package/dist/src/commands/database-types.js.map +1 -0
- package/dist/src/commands/databases.js +942 -57
- package/dist/src/commands/databases.js.map +1 -1
- package/dist/src/commands/documents.js +510 -2
- package/dist/src/commands/documents.js.map +1 -1
- package/dist/src/commands/email-templates.js +281 -0
- package/dist/src/commands/email-templates.js.map +1 -0
- package/dist/src/commands/env.js +260 -0
- package/dist/src/commands/env.js.map +1 -0
- package/dist/src/commands/group-type-configs.js +189 -0
- package/dist/src/commands/group-type-configs.js.map +1 -0
- package/dist/src/commands/groups.js +21 -18
- package/dist/src/commands/groups.js.map +1 -1
- package/dist/src/commands/init.js +649 -149
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/integrations.js +481 -20
- package/dist/src/commands/integrations.js.map +1 -1
- package/dist/src/commands/rule-sets.js +366 -0
- package/dist/src/commands/rule-sets.js.map +1 -0
- package/dist/src/commands/secrets.js +108 -0
- package/dist/src/commands/secrets.js.map +1 -0
- package/dist/src/commands/skill.js +29 -0
- package/dist/src/commands/skill.js.map +1 -0
- package/dist/src/commands/sync.js +1735 -82
- package/dist/src/commands/sync.js.map +1 -1
- package/dist/src/commands/users.js +382 -1
- package/dist/src/commands/users.js.map +1 -1
- package/dist/src/commands/webhooks.js +386 -0
- package/dist/src/commands/webhooks.js.map +1 -0
- package/dist/src/commands/workflows.js +347 -7
- package/dist/src/commands/workflows.js.map +1 -1
- package/dist/src/lib/api-client.js +714 -23
- package/dist/src/lib/api-client.js.map +1 -1
- package/dist/src/lib/config.js +51 -53
- package/dist/src/lib/config.js.map +1 -1
- package/dist/src/lib/constants.js +3 -0
- package/dist/src/lib/constants.js.map +1 -0
- package/dist/src/lib/credentials-store.js +307 -0
- package/dist/src/lib/credentials-store.js.map +1 -0
- package/dist/src/lib/env-resolver.js +121 -0
- package/dist/src/lib/env-resolver.js.map +1 -0
- package/dist/src/lib/init-config.js +87 -0
- package/dist/src/lib/init-config.js.map +1 -0
- package/dist/src/lib/paginate.js +42 -0
- package/dist/src/lib/paginate.js.map +1 -0
- package/dist/src/lib/project-config.js +209 -0
- package/dist/src/lib/project-config.js.map +1 -0
- package/dist/src/lib/skill-installer.js +135 -0
- package/dist/src/lib/skill-installer.js.map +1 -0
- package/dist/src/lib/sync-paths.js +102 -0
- package/dist/src/lib/sync-paths.js.map +1 -0
- package/dist/src/lib/template.js +199 -17
- package/dist/src/lib/template.js.map +1 -1
- package/dist/src/lib/version-check.js +172 -0
- package/dist/src/lib/version-check.js.map +1 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -150,12 +150,13 @@ Manage users within an app.
|
|
|
150
150
|
|
|
151
151
|
```bash
|
|
152
152
|
primitive users list [app-id] # List users
|
|
153
|
+
primitive users create <email> [--role admin|member] # Create/add user by email
|
|
153
154
|
primitive users invite [app-id] <email> [--role admin] # Invite user
|
|
154
155
|
primitive users remove [app-id] <user-id> # Remove user
|
|
155
156
|
primitive users set-role [app-id] <user-id> <role> # Change role
|
|
156
157
|
primitive users transfer-owner [app-id] <new-owner-id> # Transfer ownership
|
|
157
|
-
primitive users
|
|
158
|
-
primitive users invitations
|
|
158
|
+
primitive users mint-jwt <user-id> [--role <role>] # Mint test JWT (dev/test only)
|
|
159
|
+
primitive users invitations [app-id] # List pending invitations
|
|
159
160
|
```
|
|
160
161
|
|
|
161
162
|
### Waitlist
|
|
@@ -197,6 +198,26 @@ primitive integrations secrets add <id> --data '{"apiKey":"..."}'
|
|
|
197
198
|
primitive integrations secrets archive <id> <secret-id>
|
|
198
199
|
```
|
|
199
200
|
|
|
201
|
+
### Secrets
|
|
202
|
+
|
|
203
|
+
Manage encrypted app secrets (API keys, tokens, credentials). Values are encrypted at rest and never displayed after creation.
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
primitive secrets list [--app <app-id>] # List secrets (values never shown)
|
|
207
|
+
primitive secrets set <KEY> --value <value> [--summary <text>] # Create or update a secret
|
|
208
|
+
primitive secrets delete <KEY> # Delete a secret
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Examples:**
|
|
212
|
+
```bash
|
|
213
|
+
primitive secrets set OPENAI_API_KEY --value "sk-..." --summary "Production key"
|
|
214
|
+
primitive secrets set STRIPE_SECRET --value "sk_live_..."
|
|
215
|
+
primitive secrets list --json
|
|
216
|
+
primitive secrets delete STRIPE_SECRET
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Keys must be uppercase letters, digits, and underscores (e.g., `OPENAI_API_KEY`). Max 100 secrets per app, 2 KB per value. The `set` command is an upsert — it creates or updates automatically. Use `{{secrets.KEY}}` in workflows and `secrets.KEY` in CEL rules.
|
|
220
|
+
|
|
200
221
|
### Prompts
|
|
201
222
|
|
|
202
223
|
Manage LLM prompt configurations.
|
|
@@ -267,12 +288,13 @@ primitive tokens revoke <token-id> [app-id] # Revoke t
|
|
|
267
288
|
|
|
268
289
|
### Databases
|
|
269
290
|
|
|
270
|
-
Manage online databases
|
|
291
|
+
Manage online databases and permissions. `list` returns databases the user has direct access to; group-shared databases are listed via `primitive groups databases`.
|
|
271
292
|
|
|
272
293
|
```bash
|
|
273
|
-
primitive databases list [app-id] # List databases
|
|
294
|
+
primitive databases list [app-id] # List databases (direct access)
|
|
274
295
|
primitive databases create <title> [app-id] # Create database
|
|
275
296
|
primitive databases get <database-id> [app-id] # Get details
|
|
297
|
+
primitive databases update <database-id> [options] # Update title or type
|
|
276
298
|
primitive databases delete <database-id> [app-id] # Delete database
|
|
277
299
|
```
|
|
278
300
|
|
|
@@ -283,14 +305,68 @@ primitive databases permissions grant <database-id> --user-id <uid> --permission
|
|
|
283
305
|
primitive databases permissions revoke <database-id> <user-id> [app-id]
|
|
284
306
|
```
|
|
285
307
|
|
|
286
|
-
|
|
308
|
+
Permission values: `owner` (set at creation), `manager`
|
|
309
|
+
|
|
310
|
+
**Metadata:**
|
|
311
|
+
```bash
|
|
312
|
+
primitive databases metadata update <database-id> --data '{"key":"value"}' # Merge-update metadata
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Operations:**
|
|
316
|
+
```bash
|
|
317
|
+
primitive databases operations list <database-id> [app-id] # List registered operations
|
|
318
|
+
primitive databases operations execute <database-id> <op-name> --params '{}' # Execute operation
|
|
319
|
+
primitive databases operations execute <database-id> <op-name> --token <jwt> # Execute as specific user
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The `--token` flag lets you execute an operation as a specific user using a test JWT from `users mint-jwt`. Useful for testing access rules.
|
|
323
|
+
|
|
324
|
+
**Records (schema introspection):**
|
|
325
|
+
```bash
|
|
326
|
+
primitive databases records models <database-id> [app-id] # List model names
|
|
327
|
+
primitive databases records describe <database-id> <model> [app-id] # Show inferred schema
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Indexes:**
|
|
331
|
+
```bash
|
|
332
|
+
primitive databases indexes list <database-id> [--model <name>] # List indexes
|
|
333
|
+
primitive databases indexes create <database-id> <model> <field> [options] # Create index
|
|
334
|
+
primitive databases indexes drop <database-id> <model> <field> # Drop index
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Export / Import:**
|
|
338
|
+
```bash
|
|
339
|
+
primitive databases export [app-id] <database-id> --output <dir> # Export records, indexes, constraints
|
|
340
|
+
primitive databases import [app-id] <path> --overwrite --dry-run # Import from export directory
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Export creates a directory with `metadata.json`, `records.jsonl`, `indexes.json`, and `constraints.json`. Import restores records and indexes into a new or existing database. Database type config (operations, triggers, access rules) is managed separately via `primitive sync` — run `sync push` on the target app before importing.
|
|
344
|
+
|
|
345
|
+
### Documents
|
|
346
|
+
|
|
347
|
+
Manage document ownership, group permissions, and export/import.
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
primitive documents transfer-owner [app-id] <document-id> <new-owner-id> # Transfer ownership
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Group permissions on documents:**
|
|
354
|
+
```bash
|
|
355
|
+
primitive documents group-permissions list <document-id>
|
|
356
|
+
primitive documents group-permissions grant <document-id> --group-type <type> --group-id <id> --permission <perm>
|
|
357
|
+
primitive documents group-permissions revoke <document-id> <group-type> <group-id>
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Permission values: `read-write`, `reader`
|
|
361
|
+
|
|
362
|
+
**Export / Import:**
|
|
287
363
|
```bash
|
|
288
|
-
primitive
|
|
289
|
-
primitive
|
|
290
|
-
primitive
|
|
364
|
+
primitive documents export [app-id] <document-id> --output <dir> # Export Yjs state, blobs, permissions, aliases
|
|
365
|
+
primitive documents export-all [app-id] --user-id <id> --owned-only # Export all docs for a user
|
|
366
|
+
primitive documents import [app-id] <path> --overwrite --aliases overwrite|skip --dry-run # Import from export
|
|
291
367
|
```
|
|
292
368
|
|
|
293
|
-
|
|
369
|
+
Export creates a directory per document with `metadata.json`, `document.yjs` (Yjs state), `permissions.json` (for reference), and `blobs/` (attachments). Permissions are exported for reference but not restored during import — the importing admin is the new owner and manages sharing in the target app. Document IDs are preserved across import. User-scoped aliases can be restored with `--aliases overwrite` (update existing) or `--aliases skip` (keep existing, default).
|
|
294
370
|
|
|
295
371
|
### Groups
|
|
296
372
|
|
|
@@ -317,20 +393,46 @@ primitive groups members set-role <group-type> <group-id> <user-id> <role> [app-
|
|
|
317
393
|
primitive groups memberships <user-id> [app-id] # List user's group memberships
|
|
318
394
|
```
|
|
319
395
|
|
|
396
|
+
**Group resource access:**
|
|
397
|
+
```bash
|
|
398
|
+
primitive groups documents <group-type> <group-id> # List documents a group can access
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Group permissions on documents are managed via `primitive documents group-permissions` (see [Documents](#documents) above).
|
|
402
|
+
|
|
320
403
|
### Analytics
|
|
321
404
|
|
|
322
405
|
View usage analytics for an app.
|
|
323
406
|
|
|
324
407
|
```bash
|
|
325
|
-
|
|
326
|
-
primitive analytics
|
|
327
|
-
primitive analytics
|
|
328
|
-
primitive analytics
|
|
408
|
+
# Overview & active users
|
|
409
|
+
primitive analytics overview [app-id] # DAU / WAU / MAU + growth
|
|
410
|
+
primitive analytics daily-active [app-id] # Daily active users time series
|
|
411
|
+
primitive analytics rolling-active [app-id] # Rolling active users (28 points)
|
|
412
|
+
primitive analytics cohort-retention [app-id] # Weekly cohort retention matrix
|
|
413
|
+
|
|
414
|
+
# Users
|
|
415
|
+
primitive analytics top-users [app-id] # Most active users
|
|
416
|
+
primitive analytics user-search [app-id] --query <q> # Search by email or ULID
|
|
417
|
+
primitive analytics user-detail <user-ulid> [app-id] # User activity breakdown
|
|
418
|
+
primitive analytics user-snapshot <user-ulid> [app-id] # Latest context snapshot
|
|
419
|
+
|
|
420
|
+
# Events
|
|
421
|
+
primitive analytics events [app-id] # Paginated event feed
|
|
422
|
+
primitive analytics events-grouped [app-id] # Events grouped by dimension
|
|
423
|
+
|
|
424
|
+
# Features
|
|
425
|
+
primitive analytics integrations [app-id] # Integration usage metrics
|
|
426
|
+
primitive analytics workflows [app-id] # Top workflows by runs
|
|
427
|
+
primitive analytics prompts [app-id] # Top prompts by executions
|
|
329
428
|
```
|
|
330
429
|
|
|
331
|
-
**
|
|
332
|
-
- `--window-days <n>` - Time window (default
|
|
333
|
-
- `--limit <n>` - Result limit
|
|
430
|
+
**Common options:**
|
|
431
|
+
- `--window-days <n>` - Time window in days (default varies per command)
|
|
432
|
+
- `--limit <n>` - Result limit (top-users, workflows, prompts)
|
|
433
|
+
- `--group-by <dim>` - Dimension for events-grouped (action, feature, route, country, deviceType, plan, day)
|
|
434
|
+
- `--page <n>` - Page number for events feed (0-based)
|
|
435
|
+
- `--json` - Output raw JSON
|
|
334
436
|
|
|
335
437
|
### Admins (Super-Admin Only)
|
|
336
438
|
|
|
@@ -545,11 +647,13 @@ cli/tests/
|
|
|
545
647
|
config.test.ts # Credentials and config management
|
|
546
648
|
output.test.ts # Output formatting functions
|
|
547
649
|
integration/
|
|
548
|
-
api-client.test.ts
|
|
549
|
-
commands.test.ts
|
|
550
|
-
tokens.test.ts
|
|
551
|
-
databases.test.ts
|
|
552
|
-
|
|
650
|
+
api-client.test.ts # API client HTTP tests
|
|
651
|
+
commands.test.ts # CLI command tests
|
|
652
|
+
tokens.test.ts # Token lifecycle tests
|
|
653
|
+
databases.test.ts # Database CRUD, permissions, group permissions tests
|
|
654
|
+
documents.test.ts # Document group permissions, group resource listing tests
|
|
655
|
+
groups.test.ts # Group CRUD, members, memberships tests
|
|
656
|
+
classroom-e2e.test.ts # End-to-end classroom app workflow (types, rules, operations, access)
|
|
553
657
|
```
|
|
554
658
|
|
|
555
659
|
## Troubleshooting
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: primitive-platform
|
|
3
|
+
description: >
|
|
4
|
+
Expert guide for building applications on the Primitive platform. MUST be used whenever the user
|
|
5
|
+
is writing code that uses js-bao, js-bao-wss-client, primitive-app components, or any Primitive
|
|
6
|
+
platform feature (documents, databases, workflows, prompts, integrations, blobs, authentication,
|
|
7
|
+
users/groups). Also trigger whenever about to run any `primitive` CLI command (e.g., primitive sync, primitive integrations, primitive apps, primitive use) to ensure Step 0 CLI verification is performed first. After writing or modifying code that touches Primitive
|
|
8
|
+
APIs, this skill cross-references the implementation against official guides and automatically
|
|
9
|
+
corrects common mistakes. Use this skill even if the user doesn't explicitly ask for it —
|
|
10
|
+
any Primitive-related code should be validated against current best practices.
|
|
11
|
+
allowed-tools: Bash, Read, Edit, Write, Glob, Grep, Agent
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Primitive Platform Development Guide
|
|
15
|
+
|
|
16
|
+
You are an expert on the Primitive platform. Your job is to help developers write correct,
|
|
17
|
+
idiomatic Primitive code by leveraging the CLI's built-in guide system and enforcing best practices.
|
|
18
|
+
|
|
19
|
+
**The CLI guides are the single source of truth.** Never hardcode or memorize guide content —
|
|
20
|
+
always fetch the latest from the CLI.
|
|
21
|
+
|
|
22
|
+
## Step 0: Verify CLI Configuration
|
|
23
|
+
|
|
24
|
+
The Primitive CLI maintains state for the **currently active server endpoint and app**. Before
|
|
25
|
+
running any CLI commands, confirm you're targeting the correct environment:
|
|
26
|
+
|
|
27
|
+
1. **Check for a `.env` file** in the project root (e.g., `.env`, `.env.local`, `.env.development`).
|
|
28
|
+
It typically contains `PRIMITIVE_API_URL` or similar variables that set the server endpoint,
|
|
29
|
+
and may reference a specific app ID.
|
|
30
|
+
2. **Run a CLI command and read its output.** The CLI prints the active server URL and app context
|
|
31
|
+
at the top of most command outputs — verify these match the project's intended environment.
|
|
32
|
+
3. **Use inspection commands** to confirm current state:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
primitive whoami # Shows authenticated user, app ID, and server endpoint
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Why this matters:** If the CLI is pointed at the wrong server (e.g., production instead of
|
|
39
|
+
development) or the wrong app, commands like `primitive sync push` will modify the wrong
|
|
40
|
+
environment. Always verify before running mutating operations.
|
|
41
|
+
|
|
42
|
+
## Step 1: Discover Available Guides
|
|
43
|
+
|
|
44
|
+
Before writing or reviewing any Primitive code, run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
primitive guides list
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This returns the full list of available guide topics with descriptions, keywords, and use cases.
|
|
51
|
+
Use this output to determine which guides are relevant to the current task.
|
|
52
|
+
|
|
53
|
+
## Step 2: Fetch the Relevant Guides
|
|
54
|
+
|
|
55
|
+
For each relevant topic identified in Step 1, fetch the full guide:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
primitive guides get <topic>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Always fetch guide(s) BEFORE writing code.** If multiple features are involved, fetch multiple
|
|
62
|
+
guides. The guides contain:
|
|
63
|
+
- Complete API documentation with method signatures
|
|
64
|
+
- Working code examples (TypeScript/JavaScript)
|
|
65
|
+
- Common patterns and anti-patterns
|
|
66
|
+
- Configuration examples (TOML files for `primitive sync`)
|
|
67
|
+
- Decision frameworks for architecture choices
|
|
68
|
+
|
|
69
|
+
**Do not guess or assume API patterns.** If you're unsure about a method signature, parameter,
|
|
70
|
+
or pattern, fetch the guide. The guides are comprehensive and authoritative.
|
|
71
|
+
|
|
72
|
+
## Step 3: Write Code Following Guide Patterns
|
|
73
|
+
|
|
74
|
+
When writing Primitive code:
|
|
75
|
+
|
|
76
|
+
1. **Follow the patterns from the fetched guides exactly** — method names, argument order, lifecycle patterns
|
|
77
|
+
2. **Use `primitive sync`** for all backend configuration (workflows, prompts, integrations, databases)
|
|
78
|
+
3. **Configuration lives in TOML files** in version control, pushed via `primitive sync push`
|
|
79
|
+
4. **Run `pnpm codegen`** after creating or modifying js-bao models
|
|
80
|
+
|
|
81
|
+
## Step 4: Post-Code Review (Automatic)
|
|
82
|
+
|
|
83
|
+
After writing or modifying Primitive-related code, **automatically perform this review**:
|
|
84
|
+
|
|
85
|
+
### 4a. Identify What Was Written
|
|
86
|
+
Determine which Primitive features the new/modified code touches by scanning for:
|
|
87
|
+
- Import statements from `js-bao`, `js-bao-wss-client`, or `primitive-app`
|
|
88
|
+
- Primitive API calls (documents.open, databases.connect, workflows, etc.)
|
|
89
|
+
- Model definitions, schemas, queries
|
|
90
|
+
- Configuration files (TOML for sync)
|
|
91
|
+
|
|
92
|
+
### 4b. Fetch and Cross-Reference
|
|
93
|
+
Run `primitive guides list` to identify which guides cover the features used, then fetch each one:
|
|
94
|
+
```bash
|
|
95
|
+
primitive guides get <topic>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Compare the written code against the guide content:
|
|
99
|
+
- **API usage patterns** — Are methods called correctly with proper arguments?
|
|
100
|
+
- **Lifecycle management** — Are documents opened before queries? Is auth checked first?
|
|
101
|
+
- **Access control** — Are CEL expressions or permissions configured properly?
|
|
102
|
+
- **Anti-patterns** — Does the code do anything the guide explicitly warns against?
|
|
103
|
+
- **Missing steps** — Does the code need `pnpm codegen`, `primitive sync push`, or other follow-up?
|
|
104
|
+
|
|
105
|
+
### 4c. Report and Fix
|
|
106
|
+
If issues are found:
|
|
107
|
+
1. **Explain the issue** — cite the specific guide section that applies
|
|
108
|
+
2. **Show the fix** — provide corrected code
|
|
109
|
+
3. **Apply the fix** — edit the file directly (don't just suggest, actually fix it)
|
|
110
|
+
4. **Note any CLI commands needed** — e.g., `pnpm codegen` or `primitive sync push`
|
|
111
|
+
|
|
112
|
+
If no issues are found, briefly confirm the code follows best practices.
|
|
113
|
+
|
|
114
|
+
## CLI Quick Reference
|
|
115
|
+
|
|
116
|
+
Remind users of these essential commands when relevant:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Verify current configuration (DO THIS FIRST)
|
|
120
|
+
primitive whoami # Check authenticated user, app ID, and server endpoint
|
|
121
|
+
primitive status # Check active app context and server
|
|
122
|
+
# Also check .env / .env.local / .env.development in the project for
|
|
123
|
+
# PRIMITIVE_API_URL or app ID settings — these control which server the CLI targets.
|
|
124
|
+
|
|
125
|
+
# Setup
|
|
126
|
+
npm install -g primitive-admin # Install CLI
|
|
127
|
+
primitive login # Authenticate
|
|
128
|
+
primitive use "My App" # Set app context
|
|
129
|
+
|
|
130
|
+
# Guides (the most important commands for development)
|
|
131
|
+
primitive guides list # See all available guides with topics and descriptions
|
|
132
|
+
primitive guides get <topic> # Read detailed guide for a specific topic
|
|
133
|
+
|
|
134
|
+
# Configuration as Code
|
|
135
|
+
primitive sync init --dir ./config # Initialize config directory
|
|
136
|
+
primitive sync pull --dir ./config # Pull config from server
|
|
137
|
+
primitive sync push --dir ./config # Push config to server
|
|
138
|
+
primitive sync diff --dir ./config # Preview changes before push
|
|
139
|
+
|
|
140
|
+
# Common operations
|
|
141
|
+
primitive apps list # List apps
|
|
142
|
+
primitive apps create "Name" # Create app
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## When the User is Starting a New Feature
|
|
146
|
+
|
|
147
|
+
If the user describes a new feature they want to build:
|
|
148
|
+
|
|
149
|
+
1. **Verify CLI configuration** per Step 0 — confirm the server endpoint and app ID match the
|
|
150
|
+
project's intended environment before running any commands
|
|
151
|
+
2. **Run `primitive guides list`** to discover available topics
|
|
152
|
+
3. **Identify which guides are relevant** to their feature from the list output
|
|
153
|
+
4. **Fetch those guides** with `primitive guides get <topic>`
|
|
154
|
+
5. **Recommend a data modeling approach** based on the guide content. If requirements are unclear or ambiguous, **ask the user clarifying questions before proceeding** — it's much easier to get the data model right upfront than to migrate later
|
|
155
|
+
6. **Outline the implementation steps** referencing specific patterns from the guides
|
|
156
|
+
7. **Write the code** following the patterns exactly
|
|
157
|
+
8. **Review automatically** per Step 4 above
|
|
158
|
+
|
|
159
|
+
## When the User Asks "How Do I...?"
|
|
160
|
+
|
|
161
|
+
For any question about Primitive platform capabilities:
|
|
162
|
+
|
|
163
|
+
1. **Run `primitive guides list`** to find the relevant topic
|
|
164
|
+
2. **Fetch the guide**: `primitive guides get <topic>`
|
|
165
|
+
3. **Answer from the guide content** — don't guess or make up APIs
|
|
166
|
+
4. **Include working code examples** from the guide
|
|
167
|
+
5. **Point the user to the guide** for further reading: "You can see more examples by running `primitive guides get <topic>`"
|
package/dist/bin/primitive.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Command } from "commander";
|
|
2
|
+
import { Command, CommanderError } from "commander";
|
|
3
3
|
import { readFileSync } from "fs";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { dirname, resolve } from "path";
|
|
@@ -9,6 +9,8 @@ import { registerAppsCommands } from "../src/commands/apps.js";
|
|
|
9
9
|
import { registerUsersCommands } from "../src/commands/users.js";
|
|
10
10
|
import { registerWaitlistCommands } from "../src/commands/waitlist.js";
|
|
11
11
|
import { registerIntegrationsCommands } from "../src/commands/integrations.js";
|
|
12
|
+
import { registerWebhooksCommands } from "../src/commands/webhooks.js";
|
|
13
|
+
import { registerCronTriggersCommands } from "../src/commands/cron-triggers.js";
|
|
12
14
|
import { registerPromptsCommands } from "../src/commands/prompts.js";
|
|
13
15
|
import { registerWorkflowsCommands } from "../src/commands/workflows.js";
|
|
14
16
|
import { registerAdminsCommands } from "../src/commands/admins.js";
|
|
@@ -20,46 +22,189 @@ import { registerComparisonsCommands } from "../src/commands/comparisons.js";
|
|
|
20
22
|
import { registerTokensCommands } from "../src/commands/tokens.js";
|
|
21
23
|
import { registerDatabasesCommands } from "../src/commands/databases.js";
|
|
22
24
|
import { registerGroupsCommands } from "../src/commands/groups.js";
|
|
25
|
+
import { registerRuleSetsCommands } from "../src/commands/rule-sets.js";
|
|
26
|
+
import { registerGroupTypeConfigsCommands } from "../src/commands/group-type-configs.js";
|
|
27
|
+
import { registerDatabaseTypesCommands } from "../src/commands/database-types.js";
|
|
23
28
|
import { registerGuidesCommands } from "../src/commands/guides.js";
|
|
24
29
|
import { registerDocumentsCommands } from "../src/commands/documents.js";
|
|
30
|
+
import chalk from "chalk";
|
|
31
|
+
import { registerEmailTemplatesCommands } from "../src/commands/email-templates.js";
|
|
32
|
+
import { registerCollectionsCommands } from "../src/commands/collections.js";
|
|
33
|
+
import { registerCollectionTypeConfigsCommands } from "../src/commands/collection-type-configs.js";
|
|
25
34
|
import { error } from "../src/lib/output.js";
|
|
26
35
|
import { ApiError } from "../src/lib/api-client.js";
|
|
36
|
+
import { checkForUpdate } from "../src/lib/version-check.js";
|
|
37
|
+
import { loadCredentials } from "../src/lib/config.js";
|
|
38
|
+
import { checkSkillStatus } from "../src/lib/skill-installer.js";
|
|
39
|
+
import { registerSkillCommands } from "../src/commands/skill.js";
|
|
40
|
+
import { registerSecretsCommands } from "../src/commands/secrets.js";
|
|
41
|
+
import { registerBlobBucketsCommands } from "../src/commands/blob-buckets.js";
|
|
42
|
+
import { registerEnvCommands } from "../src/commands/env.js";
|
|
43
|
+
import { setCurrentEnvName, getCurrentEnvNameSafe, getProjectConfig, } from "../src/lib/env-resolver.js";
|
|
44
|
+
import { ProjectConfigError, PROJECT_CONFIG_DISPLAY_NAME, findProjectConfigPath } from "../src/lib/project-config.js";
|
|
27
45
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
46
|
const __dirname = dirname(__filename);
|
|
29
47
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, "../../package.json"), "utf-8"));
|
|
48
|
+
const isVersionFlag = process.argv.includes("--version") || process.argv.includes("-V");
|
|
49
|
+
const subcommand = process.argv[2];
|
|
50
|
+
const isJsonOutput = process.argv.includes("--json");
|
|
51
|
+
const skipHeader = isVersionFlag || isJsonOutput || subcommand === "login" || subcommand === "logout" || subcommand === "token";
|
|
52
|
+
// --env <name> is a global option that must be applied BEFORE any command
|
|
53
|
+
// code runs, because loadCredentials() and getServerUrl() both consult the
|
|
54
|
+
// active environment. Commander only parses options during .parse(), so we
|
|
55
|
+
// pre-extract it here via a tiny manual scan. The actual option is still
|
|
56
|
+
// registered on the program below so it appears in --help.
|
|
57
|
+
function preParseEnvFlag() {
|
|
58
|
+
const argv = process.argv;
|
|
59
|
+
for (let i = 2; i < argv.length; i++) {
|
|
60
|
+
const a = argv[i];
|
|
61
|
+
if (a === "--env" || a === "-e") {
|
|
62
|
+
return argv[i + 1] || null;
|
|
63
|
+
}
|
|
64
|
+
if (a.startsWith("--env="))
|
|
65
|
+
return a.slice("--env=".length);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const preParsedEnv = preParseEnvFlag();
|
|
70
|
+
if (preParsedEnv) {
|
|
71
|
+
setCurrentEnvName(preParsedEnv);
|
|
72
|
+
}
|
|
73
|
+
// Surface project-config errors loudly and early. A broken config file
|
|
74
|
+
// should fail fast with a clear message rather than producing cryptic
|
|
75
|
+
// "Not logged in" errors deeper in.
|
|
76
|
+
try {
|
|
77
|
+
getProjectConfig();
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
if (err instanceof ProjectConfigError) {
|
|
81
|
+
error(err.message);
|
|
82
|
+
if (err.path)
|
|
83
|
+
error(` at ${err.path}`);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
// If the user explicitly passed --env <name>, verify it resolves to a
|
|
89
|
+
// real environment NOW so we don't silently fall back to legacy creds.
|
|
90
|
+
if (preParsedEnv) {
|
|
91
|
+
try {
|
|
92
|
+
const project = getProjectConfig();
|
|
93
|
+
if (!project) {
|
|
94
|
+
error(`--env was given but no ${PROJECT_CONFIG_DISPLAY_NAME} was found in this directory or any parent.`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (!project.environments[preParsedEnv]) {
|
|
98
|
+
const available = Object.keys(project.environments).join(", ") || "(none)";
|
|
99
|
+
error(`Environment "${preParsedEnv}" is not defined in ${PROJECT_CONFIG_DISPLAY_NAME}. Available: ${available}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
if (err instanceof ProjectConfigError) {
|
|
105
|
+
error(err.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (!skipHeader) {
|
|
112
|
+
const creds = loadCredentials();
|
|
113
|
+
// In project mode the env may declare an appId even before login.
|
|
114
|
+
// Show that so the header is useful during `env add` / `login` flows.
|
|
115
|
+
const envNameForHeader = getCurrentEnvNameSafe();
|
|
116
|
+
let headerAppId = creds?.currentAppId;
|
|
117
|
+
let headerAppName = creds?.currentAppName;
|
|
118
|
+
if (envNameForHeader && (!headerAppId || !headerAppName)) {
|
|
119
|
+
try {
|
|
120
|
+
const envCfg = getProjectConfig()?.environments[envNameForHeader];
|
|
121
|
+
headerAppId = headerAppId || envCfg?.appId;
|
|
122
|
+
headerAppName = headerAppName || envCfg?.appName;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// ignore
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const appInfo = headerAppName
|
|
129
|
+
? `${headerAppName} (${headerAppId})`
|
|
130
|
+
: headerAppId || "None set";
|
|
131
|
+
// Prefer the active environment's apiUrl so the header stays correct
|
|
132
|
+
// even before you've logged in. Only fall back to credentials' serverUrl
|
|
133
|
+
// in legacy mode.
|
|
134
|
+
let server = creds?.serverUrl;
|
|
135
|
+
if (envNameForHeader) {
|
|
136
|
+
try {
|
|
137
|
+
const env = getProjectConfig()?.environments[envNameForHeader];
|
|
138
|
+
if (env)
|
|
139
|
+
server = env.apiUrl;
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// fall through
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
server = server || "Not configured";
|
|
146
|
+
const projectConfigPath = findProjectConfigPath();
|
|
147
|
+
const envLabel = envNameForHeader
|
|
148
|
+
? ` | Env: ${envNameForHeader}`
|
|
149
|
+
: projectConfigPath
|
|
150
|
+
? " | Env: (none selected)"
|
|
151
|
+
: "";
|
|
152
|
+
console.log(chalk.dim(`CLI Version: ${pkg.version}${envLabel} | App: ${appInfo} | Server: ${server}`));
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
30
155
|
const program = new Command();
|
|
31
156
|
program
|
|
32
157
|
.name("primitive")
|
|
158
|
+
.exitOverride()
|
|
33
159
|
.description(`CLI for administering Primitive applications.
|
|
34
160
|
|
|
35
161
|
Manage apps, users, integrations, prompts, workflows, and more from the command line.
|
|
36
162
|
Supports TOML-based configuration sync for version-controlled app settings.`)
|
|
37
163
|
.version(pkg.version)
|
|
164
|
+
// Global --env flag. Parsed above in preParseEnvFlag too; declaring it
|
|
165
|
+
// here so commander shows it in help output and forwards it to
|
|
166
|
+
// subcommands.
|
|
167
|
+
.option("-e, --env <name>", "Named environment from .primitive/config.json (e.g. dev, prod)")
|
|
38
168
|
.addHelpText("after", `
|
|
39
169
|
Examples:
|
|
40
|
-
$ primitive
|
|
41
|
-
$ primitive
|
|
42
|
-
$ primitive
|
|
43
|
-
$ primitive
|
|
44
|
-
$ primitive
|
|
170
|
+
$ primitive init my-app # Scaffold a new project + .primitive/config.json
|
|
171
|
+
$ primitive env list # Show environments
|
|
172
|
+
$ primitive env add prod --api-url ... # Add a new environment
|
|
173
|
+
$ primitive --env prod users list # Run a command against a specific env
|
|
174
|
+
$ primitive login # Authenticate via browser OAuth
|
|
175
|
+
$ primitive sync pull # Pull config (auto per-env path)
|
|
45
176
|
|
|
46
|
-
|
|
47
|
-
|
|
177
|
+
Project configuration (.primitive/config.json):
|
|
178
|
+
In a directory with a .primitive/config.json (or any subdirectory of one),
|
|
179
|
+
the CLI operates in project mode: credentials and sync state live under
|
|
180
|
+
<projectRoot>/.primitive/ and each named environment is isolated.
|
|
181
|
+
Without a .primitive/config.json the CLI runs in legacy mode, reading
|
|
182
|
+
credentials from ~/.primitive/credentials.json.
|
|
183
|
+
|
|
184
|
+
Environment selection (project mode):
|
|
185
|
+
1. --env <name> flag
|
|
186
|
+
2. PRIMITIVE_ENV env var
|
|
187
|
+
3. "defaultEnvironment" in .primitive/config.json
|
|
188
|
+
4. The only environment, if there's exactly one
|
|
48
189
|
|
|
49
190
|
App Context:
|
|
50
|
-
|
|
51
|
-
|
|
191
|
+
Bind an app to an environment via "appId" in .primitive/config.json, or
|
|
192
|
+
run 'primitive use <app>' to set one in credentials. Most commands accept
|
|
193
|
+
--app to override.
|
|
52
194
|
|
|
53
195
|
Documentation:
|
|
54
196
|
See https://primitive-labs.github.io/primitive-docs/ for full documentation.
|
|
55
197
|
`);
|
|
56
198
|
// Register all command groups (init first, then logically grouped)
|
|
57
199
|
registerInitCommand(program);
|
|
200
|
+
registerEnvCommands(program);
|
|
58
201
|
registerAuthCommands(program);
|
|
59
202
|
registerAppsCommands(program);
|
|
60
203
|
registerUsersCommands(program);
|
|
61
204
|
registerWaitlistCommands(program);
|
|
62
205
|
registerIntegrationsCommands(program);
|
|
206
|
+
registerWebhooksCommands(program);
|
|
207
|
+
registerCronTriggersCommands(program);
|
|
63
208
|
registerPromptsCommands(program);
|
|
64
209
|
registerWorkflowsCommands(program);
|
|
65
210
|
registerAdminsCommands(program);
|
|
@@ -71,24 +216,56 @@ registerComparisonsCommands(program);
|
|
|
71
216
|
registerTokensCommands(program);
|
|
72
217
|
registerDatabasesCommands(program);
|
|
73
218
|
registerGroupsCommands(program);
|
|
219
|
+
registerRuleSetsCommands(program);
|
|
220
|
+
registerGroupTypeConfigsCommands(program);
|
|
221
|
+
registerDatabaseTypesCommands(program);
|
|
74
222
|
registerGuidesCommands(program);
|
|
75
223
|
registerDocumentsCommands(program);
|
|
224
|
+
registerEmailTemplatesCommands(program);
|
|
225
|
+
registerCollectionsCommands(program);
|
|
226
|
+
registerCollectionTypeConfigsCommands(program);
|
|
227
|
+
registerSkillCommands(program);
|
|
228
|
+
registerSecretsCommands(program);
|
|
229
|
+
registerBlobBucketsCommands(program);
|
|
76
230
|
// Global error handler
|
|
77
231
|
program.hook("preAction", () => {
|
|
78
232
|
// Reset API client state before each command
|
|
79
233
|
});
|
|
234
|
+
// Commands that should never show post-command messages (update check, skill status)
|
|
235
|
+
const suppressPostMessages = isJsonOutput || subcommand === "token";
|
|
236
|
+
async function runPostCommandMessages() {
|
|
237
|
+
if (suppressPostMessages)
|
|
238
|
+
return;
|
|
239
|
+
// Claude Code skill: auto-update if installed, or show hint if not.
|
|
240
|
+
// Skip for `init` (handles its own prompt), `skill` (explicit management),
|
|
241
|
+
// and non-interactive contexts (--json, --version, token, etc.).
|
|
242
|
+
if (!skipHeader && subcommand !== "skill" && subcommand !== "init") {
|
|
243
|
+
await checkSkillStatus();
|
|
244
|
+
}
|
|
245
|
+
await checkForUpdate(pkg.version);
|
|
246
|
+
}
|
|
80
247
|
// Parse and execute
|
|
81
|
-
program.parseAsync(process.argv)
|
|
248
|
+
program.parseAsync(process.argv)
|
|
249
|
+
.then(async () => {
|
|
250
|
+
await runPostCommandMessages();
|
|
251
|
+
})
|
|
252
|
+
.catch(async (err) => {
|
|
253
|
+
// Commander throws CommanderError for help display, version flag, etc.
|
|
254
|
+
// Run post-command messages then exit with the intended code.
|
|
255
|
+
if (err instanceof CommanderError) {
|
|
256
|
+
await runPostCommandMessages();
|
|
257
|
+
process.exit(err.exitCode);
|
|
258
|
+
}
|
|
82
259
|
if (err instanceof ApiError) {
|
|
83
260
|
error(err.message);
|
|
84
261
|
if (err.statusCode === 401) {
|
|
85
262
|
error("Try running 'primitive login' to authenticate.");
|
|
86
263
|
}
|
|
264
|
+
await runPostCommandMessages();
|
|
87
265
|
process.exit(err.statusCode === 401 ? 2 : 1);
|
|
88
266
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
267
|
+
error(err.message || "An unexpected error occurred");
|
|
268
|
+
await runPostCommandMessages();
|
|
269
|
+
process.exit(1);
|
|
93
270
|
});
|
|
94
271
|
//# sourceMappingURL=primitive.js.map
|