frontpage-host 0.1.24 → 0.1.25
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/PEOPLE.md +530 -131
- package/dist/commands/push.js +35 -12
- package/dist/registry.js +1 -1
- package/package.json +2 -2
package/PEOPLE.md
CHANGED
|
@@ -1,169 +1,568 @@
|
|
|
1
|
-
# People:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
frontpage people
|
|
63
|
-
frontpage people
|
|
64
|
-
frontpage people
|
|
65
|
-
frontpage people membership
|
|
66
|
-
frontpage people membership
|
|
67
|
-
frontpage people
|
|
68
|
-
frontpage people
|
|
69
|
-
frontpage people
|
|
70
|
-
frontpage people
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
frontpage people rules enable --id RULE_ID --site demo --json
|
|
79
|
-
frontpage people rules pause --id RULE_ID --site demo --json
|
|
80
|
-
frontpage people rules runs --rule RULE_ID --site demo --json
|
|
81
|
-
frontpage people rules retry --run RUN_ID --site demo --json
|
|
82
|
-
frontpage people person permission --person PERSON_ID --allowed true --site demo --json
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Use this `rule.json`, replacing IDs and the form slug with values from `overview` and `sources`:
|
|
86
|
-
|
|
87
|
-
```json
|
|
1
|
+
# People CRM: CLI and agent guide
|
|
2
|
+
|
|
3
|
+
**One contact record. Many groups. Clear progress.**
|
|
4
|
+
|
|
5
|
+
People keeps a business’s contacts, notes, follow-ups, and history together. Groups organize contacts; optional stages track progress inside each group. Rules can update both and send a saved email or series. One-off campaigns stay in Emails.
|
|
6
|
+
|
|
7
|
+
## Get started
|
|
8
|
+
|
|
9
|
+
Use the current `frontpage-host` CLI. Sign in, then choose a site using `--site` on each command or `frontpage use YOUR_SITE`. A site is its subdomain slug, such as `acme`, not a full URL or UUID.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
npm install -g frontpage-host@latest
|
|
13
|
+
frontpage login
|
|
14
|
+
frontpage people overview --site YOUR_SITE --json
|
|
15
|
+
frontpage people sources --site YOUR_SITE --json
|
|
16
|
+
frontpage people list --site YOUR_SITE --limit 50 --json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Replace `YOUR_SITE` and all uppercase ID placeholders before running an example. Copy IDs from real results: groups, stages, people, rules, templates, and series use UUIDs. Forms use a stable form slug; products use their item ID. Do not invent IDs.
|
|
20
|
+
|
|
21
|
+
**Access stays scoped to your site.**
|
|
22
|
+
|
|
23
|
+
People requires Growth or above. Public API and CLI access requires Scale, Enterprise, or Internal, plus membership in the chosen site. Viewers can read; editors and admins can change data. A request cannot select another tenant through its JSON body.
|
|
24
|
+
|
|
25
|
+
`--json` returns structured output. Complex data accepts `--file payload.json` or `--data 'JSON'`. Use `frontpage help people` and `frontpage help mail` for command flags.
|
|
26
|
+
|
|
27
|
+
## Contacts, notes, and history
|
|
28
|
+
|
|
29
|
+
Each contact needs a valid email or phone. A name is optional. If you supply both identifiers, both must be valid. Matching email or phone updates the existing contact; conflicting identities fail instead of merging two people.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
frontpage people add --email alex@example.com --name "Alex Rivera" --site YOUR_SITE --json
|
|
33
|
+
frontpage people get PERSON_ID --site YOUR_SITE --json
|
|
34
|
+
frontpage people set PERSON_ID --name "Alex Rivera" --phone +12025550166 --site YOUR_SITE --json
|
|
35
|
+
frontpage people set PERSON_ID --address-name "Alex Rivera" --line1 "12 Main St" --line2 "Suite 4" --city Austin --region TX --postal 78701 --country US --site YOUR_SITE --json
|
|
36
|
+
frontpage people note PERSON_ID --body "Asked for a quote. Call on Friday." --site YOUR_SITE --json
|
|
37
|
+
frontpage people follow-up PERSON_ID --at 2026-11-06T15:00:00Z --site YOUR_SITE --json
|
|
38
|
+
frontpage people list --follow-up due --site YOUR_SITE --json
|
|
39
|
+
frontpage people hide PERSON_ID --site YOUR_SITE --json
|
|
40
|
+
frontpage people unhide PERSON_ID --site YOUR_SITE --json
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- **Details:** name, email, phone, mailing address, original source, first seen, and last seen. Address fields include recipient name, line 1, line 2, city, region, postal code, and country.
|
|
44
|
+
- **Notes:** `people note PERSON_ID --body "…"` adds a manual note. `people rm-note PERSON_ID --event NOTE_EVENT_ID` removes an owner’s manual note. Source submissions and payments are not removable notes.
|
|
45
|
+
- **Follow-up:** set an ISO date with `--at`; clear it with `people follow-up PERSON_ID --clear`. The person page highlights overdue dates.
|
|
46
|
+
- **Activity:** `people get` returns the recent 50 events. The person page shows only populated categories with counts. Group and stage pages do not show a separate activity feed.
|
|
47
|
+
- **Hide and restore:** hide is recoverable. It retains identity, history, and membership. It excludes the person from campaign audiences and automated emails. Later source ingest may restore them.
|
|
48
|
+
|
|
49
|
+
### Find and export people
|
|
50
|
+
|
|
51
|
+
`people list` and `people export` accept search, group, stage, source, form, follow-up, hidden state, sort, recency, and legacy status/tag filters. List defaults to 50 contacts, supports up to 200 per page, and returns `nextCursor`. Pass that cursor to `--cursor` to continue. Export writes UTF-8 CSV; it is a contact export, not a complete activity archive.
|
|
52
|
+
|
|
53
|
+
## Groups: organize people
|
|
54
|
+
|
|
55
|
+
A person can belong to several groups at once. Use a group for Leads, Customers, Newsletter, or another useful list. Rename a group freely: its ID stays the same.
|
|
56
|
+
|
|
57
|
+
### Add people yourself or with rules
|
|
58
|
+
|
|
59
|
+
A manual group keeps membership until you, the agent, an import, or a rule changes it. These commands also manage the groups shown in the People filter.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
frontpage people groups create --label Leads --color sky --mode manual --site YOUR_SITE --json
|
|
63
|
+
frontpage people groups list --site YOUR_SITE --json
|
|
64
|
+
frontpage people groups update --id GROUP_ID --label "New enquiries" --site YOUR_SITE --json
|
|
65
|
+
frontpage people membership add --person PERSON_ID --group GROUP_ID --site YOUR_SITE --json
|
|
66
|
+
frontpage people membership get --person PERSON_ID --site YOUR_SITE --json
|
|
67
|
+
frontpage people list --group GROUP_ID --site YOUR_SITE --json
|
|
68
|
+
frontpage people export --group GROUP_ID --out leads.csv --site YOUR_SITE
|
|
69
|
+
frontpage people groups archive --id GROUP_ID --site YOUR_SITE --json
|
|
70
|
+
frontpage people groups restore --id GROUP_ID --site YOUR_SITE --json
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Keep a group matched to conditions
|
|
74
|
+
|
|
75
|
+
An automatic group uses saved contact facts. The worker checks contacts in rotating batches and updates membership as those facts or time change. Preview first. Save this as `repeat-buyers.json`:
|
|
76
|
+
|
|
77
|
+
```
|
|
88
78
|
{
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
79
|
+
"label": "Repeat buyers",
|
|
80
|
+
"color": "emerald",
|
|
81
|
+
"mode": "conditions",
|
|
82
|
+
"conditions": {
|
|
83
|
+
"all": [
|
|
84
|
+
{
|
|
85
|
+
"field": "purchaseCount",
|
|
86
|
+
"op": "gte",
|
|
87
|
+
"value": 2
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"field": "lastPurchaseDays",
|
|
91
|
+
"op": "lte",
|
|
92
|
+
"value": 90
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
frontpage people groups preview --file repeat-buyers.json --site YOUR_SITE --json
|
|
101
|
+
frontpage people groups create --file repeat-buyers.json --site YOUR_SITE --json
|
|
102
|
+
frontpage people membership override --person PERSON_ID --group GROUP_ID --override include --site YOUR_SITE --json
|
|
103
|
+
frontpage people membership override --person PERSON_ID --group GROUP_ID --override exclude --site YOUR_SITE --json
|
|
104
|
+
frontpage people membership override --person PERSON_ID --group GROUP_ID --override auto --site YOUR_SITE --json
|
|
105
|
+
frontpage people membership hold --person PERSON_ID --group GROUP_ID --hold true --site YOUR_SITE --json
|
|
106
|
+
frontpage people membership hold --person PERSON_ID --group GROUP_ID --hold false --site YOUR_SITE --json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`include` keeps one person in an automatic group; `exclude` keeps them out. `auto` clears the exception. A hold pauses automatic changes and that group’s attached email for this person. It does not prevent an owner from making an explicit change.
|
|
110
|
+
|
|
111
|
+
**Preserve existing records.**
|
|
112
|
+
|
|
113
|
+
Existing tags become manual groups. Existing statuses become stages in Customer progress. Setup keeps person IDs, notes, follow-ups, hidden state, and history, and sends no email. Old tag/status CLI commands remain supported. New integrations should use group and stage IDs.
|
|
114
|
+
|
|
115
|
+
Archive groups instead of deleting their history. Assignments stay saved; related rules pause. Restoring a group does not reactivate those rules. Review and activate them explicitly.
|
|
116
|
+
|
|
117
|
+
## Stages: track progress within a group
|
|
118
|
+
|
|
119
|
+
Stages are optional. A person can have one current stage per group, or no stage. A simple funnel might use New → Working → Customer. Save the group before adding stages or their rules.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
frontpage people stages create --group GROUP_ID --label New --color sky --position 0 --site YOUR_SITE --json
|
|
123
|
+
frontpage people stages create --group GROUP_ID --label Working --color yellow --position 1 --site YOUR_SITE --json
|
|
124
|
+
frontpage people stages create --group GROUP_ID --label Customer --color emerald --position 2 --terminal true --site YOUR_SITE --json
|
|
125
|
+
frontpage people membership stage --person PERSON_ID --group GROUP_ID --stage STAGE_ID --site YOUR_SITE --json
|
|
126
|
+
frontpage people membership advance --person PERSON_ID --group GROUP_ID --site YOUR_SITE --json
|
|
127
|
+
frontpage people stages reorder --data '{"groupId":"GROUP_ID","stageIds":["NEW_ID","WORKING_ID","CUSTOMER_ID"]}' --site YOUR_SITE --json
|
|
128
|
+
frontpage people membership stage --data '{"personId":"PERSON_ID","groupId":"GROUP_ID","stageId":null}' --site YOUR_SITE --json
|
|
129
|
+
frontpage people stages archive --id STAGE_ID --group GROUP_ID --site YOUR_SITE --json
|
|
130
|
+
frontpage people stages restore --id STAGE_ID --group GROUP_ID --site YOUR_SITE --json
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
- Joining a group starts at its first stage when no previous stage exists. Rejoining keeps the prior stage; `reset:true` on add/move starts again.
|
|
134
|
+
- `membership.advance` follows ordered nonterminal stages. It stops when the current stage is final. Choose a final stage explicitly with `membership.stage` or a rule’s `set_stage` action.
|
|
135
|
+
- `stageId:null` clears the stage while keeping group membership.
|
|
136
|
+
- `stages.reorder` requires every active stage once. A duplicate, incomplete, or stale order fails without partially changing positions.
|
|
137
|
+
- Archiving a stage keeps its assignments and history and pauses related rules. Restoring it leaves those rules paused.
|
|
138
|
+
|
|
139
|
+
## Rules: When / If / Then
|
|
140
|
+
|
|
141
|
+
Rules live beside their group or stage. Each has a start event, optional checks, and ordered actions. The editor saves new rules paused. Activate only after reviewing the result; activation applies to future events, not past submissions.
|
|
142
|
+
|
|
143
|
+
### Example: follow up on an enquiry
|
|
144
|
+
|
|
145
|
+
Use a form slug from `people sources`. Save this as `enquiry-rule.json`:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
{
|
|
149
|
+
"name": "Follow up on contact enquiries",
|
|
150
|
+
"trigger": {
|
|
151
|
+
"event": "form.submitted",
|
|
152
|
+
"formSlug": "contact",
|
|
153
|
+
"owner": {
|
|
154
|
+
"groupId": "GROUP_ID"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"conditions": {
|
|
158
|
+
"field": "email",
|
|
159
|
+
"op": "present"
|
|
160
|
+
},
|
|
92
161
|
"actions": [
|
|
93
|
-
{
|
|
94
|
-
|
|
162
|
+
{
|
|
163
|
+
"type": "add_group",
|
|
164
|
+
"groupId": "GROUP_ID"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"type": "set_follow_up",
|
|
168
|
+
"minutes": 1440
|
|
169
|
+
}
|
|
95
170
|
],
|
|
96
171
|
"enabled": false
|
|
97
172
|
}
|
|
98
173
|
```
|
|
99
174
|
|
|
100
|
-
|
|
175
|
+
```
|
|
176
|
+
frontpage people rules validate --file enquiry-rule.json --site YOUR_SITE --json
|
|
177
|
+
frontpage people rules preview --file enquiry-rule.json --person PERSON_ID --site YOUR_SITE --json
|
|
178
|
+
frontpage people rules create --file enquiry-rule.json --site YOUR_SITE --json
|
|
179
|
+
frontpage people rules enable --id RULE_ID --site YOUR_SITE --json
|
|
180
|
+
frontpage people rules pause --id RULE_ID --site YOUR_SITE --json
|
|
181
|
+
frontpage people rules update --id RULE_ID --file enquiry-rule.json --site YOUR_SITE --json
|
|
182
|
+
frontpage people rules runs --rule RULE_ID --limit 50 --site YOUR_SITE --json
|
|
183
|
+
frontpage people rules retry --run RUN_ID --site YOUR_SITE --json
|
|
184
|
+
frontpage people rules archive --id RULE_ID --site YOUR_SITE --json
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`trigger.owner` only places the rule in the editor. It never limits who can start the event. A form rule can belong to a stage without requiring the form submission to contain a group ID. Use `conditions` to limit it to existing members. Use `trigger.groupId` and `trigger.stageId` for membership events themselves.
|
|
101
188
|
|
|
102
|
-
|
|
103
|
-
|
|
189
|
+
### When: choose a real event
|
|
190
|
+
|
|
191
|
+
| Event | When it occurs | Selector |
|
|
192
|
+
| ------------------------------------------------------------- | ---------------------------------------------- | ------------------------------------------- |
|
|
193
|
+
| person.added / person.updated | A contact is added or edited. | — |
|
|
194
|
+
| form.submitted | A form receives a submission. | formSlug optional |
|
|
195
|
+
| booking.requested | Someone requests an appointment. | — |
|
|
196
|
+
| signup.subscribed / signup.resubscribed / signup.unsubscribed | A mailing-list subscription changes. | — |
|
|
197
|
+
| payment.paid / payment.refunded | A payment succeeds or is refunded. | — |
|
|
198
|
+
| order.paid / order.refunded / order.fulfilled | A store order is paid, refunded, or fulfilled. | productId optional on order.paid |
|
|
199
|
+
| square\_order.paid | A Square order is paid. | — |
|
|
200
|
+
| group.joined / group.left | A person enters or leaves a group. | groupId required |
|
|
201
|
+
| stage.entered | A person enters a stage. | groupId and stageId required |
|
|
202
|
+
| group.elapsed | A person spends the chosen time in a group. | groupId and delayMinutes required |
|
|
203
|
+
| stage.elapsed | A person spends the chosen time in a stage. | groupId, stageId, and delayMinutes required |
|
|
204
|
+
|
|
205
|
+
Forms are detected from the current site’s module instances. Product choices come from active store items. Renaming a display label keeps the saved slug/item ID, so the rule keeps working. Deleting or replacing that ID requires updating the rule; validation rejects a missing source.
|
|
206
|
+
|
|
207
|
+
### If: add only useful checks
|
|
208
|
+
|
|
209
|
+
Leave conditions `null` to run for every matching event. Use `{all:[…]}` when every check must match, or `{any:[…]}` when one is enough. Sets accept 1–20 children, up to four levels and 100 total clauses.
|
|
210
|
+
|
|
211
|
+
| Field or shape | Meaning | Comparison |
|
|
212
|
+
| ---------------------------------- | ------------------------------------------------------------------------------ | ------------------------------ |
|
|
213
|
+
| purchaseCount | Number of recorded paid order/payment events. | is, is\_not, gte, lte, present |
|
|
214
|
+
| spentCents | Total amountCents from those paid events. Integer cents, not a decimal amount. | is, is\_not, gte, lte, present |
|
|
215
|
+
| lastPurchaseDays | Whole days since the latest recorded paid event. | is, is\_not, gte, lte, present |
|
|
216
|
+
| lastSeenDays | Whole days since the contact’s last CRM activity. | is, is\_not, gte, lte, present |
|
|
217
|
+
| email / phone / name | Contact details. present needs no value. | is, is\_not, contains, present |
|
|
218
|
+
| formSlug + submitted | Whether this person submitted the named form. | submitted: true or false |
|
|
219
|
+
| groupId + member; stageId optional | Whether a person belongs to a group or stage. | member: true or false |
|
|
220
|
+
| subscribed | Existing advanced subscription condition. Send eligibility remains separate. | is, is\_not with boolean value |
|
|
221
|
+
|
|
222
|
+
Numeric values are whole numbers from 0 to 1,000,000,000. Amounts use integer cents: 2500 means 25.00. Purchase facts count recorded paid order/payment events and sum their amounts; they are not net revenue after refunds and do not convert currencies. Last activity means CRM activity, not anonymous page views. Unknown facts do not remove a person from an automatic group.
|
|
223
|
+
|
|
224
|
+
The simple editor offers Purchases, Activity, and Contact details. Existing advanced conditions—including nested sets and membership checks—remain available through the CLI and agent and stay intact during UI edits. Email permission is handled separately before each send.
|
|
225
|
+
|
|
226
|
+
### Then: perform ordered actions
|
|
227
|
+
|
|
228
|
+
| Action type | Fields | Result |
|
|
229
|
+
| -------------------------- | ------------------------------- | --------------------------------------------------------------- |
|
|
230
|
+
| add\_group / remove\_group | groupId | Add to or remove from a manual group. |
|
|
231
|
+
| move\_group | fromGroupId, groupId | Move between manual groups. |
|
|
232
|
+
| set\_stage | groupId, stageId, fromStageIds? | Set a stage; an optional guard limits eligible starting stages. |
|
|
233
|
+
| advance\_stage | groupId, fromStageIds? | Advance through nonterminal stages. |
|
|
234
|
+
| set\_follow\_up | minutes | Set a follow-up after this many minutes. |
|
|
235
|
+
| clear\_follow\_up | — | Clear the person’s follow-up. |
|
|
236
|
+
| send\_template | templateId | Send a saved template if eligible. |
|
|
237
|
+
| start\_series | seriesId | Start an active series with saved steps. |
|
|
238
|
+
| cancel\_series | seriesId | Stop this group’s enrollment; requires a group/stage trigger. |
|
|
239
|
+
|
|
240
|
+
A rule accepts 1–10 actions. Add/remove/move actions target manual groups; automatic groups use conditions and owner exceptions. `fromStageIds` prevents a late event from resetting someone who has moved further through the funnel.
|
|
241
|
+
|
|
242
|
+
### Example: move a paid lead to Customer
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
{
|
|
246
|
+
"name": "Mark paid leads as customers",
|
|
247
|
+
"trigger": {
|
|
248
|
+
"event": "order.paid",
|
|
249
|
+
"owner": {
|
|
250
|
+
"groupId": "GROUP_ID",
|
|
251
|
+
"stageId": "CUSTOMER_ID"
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
"conditions": {
|
|
255
|
+
"groupId": "GROUP_ID",
|
|
256
|
+
"member": true
|
|
257
|
+
},
|
|
258
|
+
"actions": [
|
|
259
|
+
{
|
|
260
|
+
"type": "set_stage",
|
|
261
|
+
"groupId": "GROUP_ID",
|
|
262
|
+
"stageId": "CUSTOMER_ID",
|
|
263
|
+
"fromStageIds": [
|
|
264
|
+
"NEW_ID",
|
|
265
|
+
"WORKING_ID"
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
"enabled": false
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Example: send a follow-up after two days
|
|
274
|
+
|
|
275
|
+
```
|
|
276
|
+
{
|
|
277
|
+
"name": "Follow up after two days",
|
|
278
|
+
"trigger": {
|
|
279
|
+
"event": "stage.elapsed",
|
|
280
|
+
"groupId": "GROUP_ID",
|
|
281
|
+
"stageId": "WORKING_ID",
|
|
282
|
+
"delayMinutes": 2880,
|
|
283
|
+
"owner": {
|
|
284
|
+
"groupId": "GROUP_ID",
|
|
285
|
+
"stageId": "WORKING_ID"
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
"conditions": null,
|
|
289
|
+
"actions": [
|
|
290
|
+
{
|
|
291
|
+
"type": "send_template",
|
|
292
|
+
"templateId": "TEMPLATE_ID"
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
"enabled": false
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Timed rules use minutes: 2880 is two days. They run through the worker, not a browser timer. Use `rules runs` to inspect outcomes and `rules retry` to retry eligible failed work. Rule edits create a new version; changed or paused rules cannot replay old runs.
|
|
300
|
+
|
|
301
|
+
## Attach an email to a group or stage
|
|
302
|
+
|
|
303
|
+
Use one action, **Attach an email**, then choose a saved email template or an active series. An attachment creates a normal editable rule on group entry or stage entry. Campaigns are one-off emails and cannot be selected for automation.
|
|
304
|
+
|
|
305
|
+
Save a reusable template as `welcome.json`:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
{
|
|
309
|
+
"name": "Welcome to our business",
|
|
310
|
+
"subject": "Thanks for getting in touch",
|
|
311
|
+
"preheader": "Here is what happens next.",
|
|
312
|
+
"blocks": [
|
|
313
|
+
{
|
|
314
|
+
"type": "paragraph",
|
|
315
|
+
"text": "Thanks for getting in touch. Our team will contact you soon."
|
|
316
|
+
}
|
|
317
|
+
]
|
|
318
|
+
}
|
|
104
319
|
```
|
|
105
320
|
|
|
106
|
-
```
|
|
107
|
-
frontpage
|
|
108
|
-
frontpage people
|
|
109
|
-
frontpage
|
|
110
|
-
frontpage
|
|
111
|
-
frontpage
|
|
321
|
+
```
|
|
322
|
+
frontpage mail template create --file welcome.json --site YOUR_SITE --json
|
|
323
|
+
frontpage people attachments create --group GROUP_ID --email-kind template --email-id TEMPLATE_ID --enabled false --site YOUR_SITE --json
|
|
324
|
+
frontpage mail series create --name "Getting started" --site YOUR_SITE --json
|
|
325
|
+
frontpage mail series steps set SERIES_ID --step template=TEMPLATE_ID,delay=0 --site YOUR_SITE --json
|
|
326
|
+
frontpage mail series activate SERIES_ID --site YOUR_SITE --json
|
|
327
|
+
frontpage people attachments create --group GROUP_ID --stage STAGE_ID --email-kind series --email-id SERIES_ID --enabled false --site YOUR_SITE --json
|
|
328
|
+
frontpage people rules enable --id ATTACHMENT_RULE_ID --site YOUR_SITE --json
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Copy the attachment’s returned rule ID before enabling it. Pass `--enabled false` to review it first. Omitting `enabled` on the API keeps its existing enabled-by-default behavior; the editor explicitly saves paused attachments.
|
|
332
|
+
|
|
333
|
+
- A real group/stage reentry can send another template. Duplicate events and action retries do not send that same action twice.
|
|
334
|
+
- An existing email/series enrollment does not restart when the person reenters.
|
|
335
|
+
- Removing someone from a group does not automatically cancel a series. Add `cancel_series` to a group/stage rule when that is the intended behavior; it affects that group’s enrollment only.
|
|
336
|
+
- Pause or archive the attachment rule to detach it. This does not erase its history.
|
|
337
|
+
|
|
338
|
+
See the [Email guide](/developers/mail) for content blocks, template variables, series step delays, sender settings, and email limits.
|
|
339
|
+
|
|
340
|
+
## Email permission is independent of groups
|
|
341
|
+
|
|
342
|
+
Group membership never grants email permission. A person who opts out stays in their groups and stages, while automated marketing emails and campaigns stop. Every delivery checks permission again, so an unsubscribe after audience selection still stops delivery.
|
|
343
|
+
|
|
344
|
+
```
|
|
345
|
+
frontpage people person permission --person PERSON_ID --allowed false --site YOUR_SITE --json
|
|
346
|
+
frontpage people membership get --person PERSON_ID --site YOUR_SITE --json
|
|
347
|
+
# Only record permission when the person has actually given it:
|
|
348
|
+
frontpage people person permission --person PERSON_ID --allowed true --site YOUR_SITE --json
|
|
112
349
|
```
|
|
113
350
|
|
|
114
|
-
CSV
|
|
351
|
+
`membership.get` includes email eligibility and the reason a contact cannot receive mail. Missing email, hidden state, missing permission, saved unsubscribe, and suppression can block delivery. An owner can record a new permission decision, but cannot reverse a mailing-list unsubscribe or suppression. CSV values cannot reverse an existing opt-out, including a recorded false decision. Do not add a permission check to every group or stage; delivery already enforces it.
|
|
115
352
|
|
|
116
|
-
|
|
117
|
-
|
|
353
|
+
## Import a customer list from CSV
|
|
354
|
+
|
|
355
|
+
CSV imports accept up to 5 MB and 10,000 contact rows. Quoted commas, quoted newlines, UTF-8 BOM, and CRLF are supported. Larger lists need separate files; the UI does not render an unbounded list of failures.
|
|
356
|
+
|
|
357
|
+
### 1. Upload and map columns
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
Full name,Email address,Phone,Permission
|
|
361
|
+
Alex Rivera,alex@example.com,,
|
|
362
|
+
"Pat, Jr.",pat@example.com,+12025550166,yes
|
|
363
|
+
Phone customer,,+12025550199,
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Map exact header names in `mapping.json`. The permission column is optional; omit it when permission is unknown. `yes`/`true` records existing permission and `no`/`false` records no permission.
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
{
|
|
370
|
+
"mapping": {
|
|
371
|
+
"name": "Full name",
|
|
372
|
+
"email": "Email address",
|
|
373
|
+
"phone": "Phone",
|
|
374
|
+
"emailPermission": "Permission"
|
|
375
|
+
},
|
|
376
|
+
"groupIds": [
|
|
377
|
+
"GROUP_ID"
|
|
378
|
+
]
|
|
379
|
+
}
|
|
118
380
|
```
|
|
119
381
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
382
|
+
### 2. Review matching and errors
|
|
383
|
+
|
|
384
|
+
Preview returns real new/existing counts, normalized rows, fields that can fill blank saved details, duplicates, and errors. A name is optional; email or phone is required. Existing nonblank contact details stay saved. A repeated identity collapses into one contact; conflicting email/phone matches fail instead of merging different people.
|
|
385
|
+
|
|
386
|
+
| Problem | How to fix it |
|
|
387
|
+
| ------------------------------------ | --------------------------------------------------------------------------------------------- |
|
|
388
|
+
| Invalid email | Use a complete email, or clear it when a valid phone exists. |
|
|
389
|
+
| Invalid phone | Use a valid full number, preferably with country code, or clear it when a valid email exists. |
|
|
390
|
+
| Email or phone missing | Provide at least one contact identifier. |
|
|
391
|
+
| Conflicting identities | Correct the CSV or the saved contact. The importer will not merge two people. |
|
|
392
|
+
| Invalid permission or column mapping | Use true/false or yes/no; map existing unique headers. |
|
|
393
|
+
|
|
394
|
+
The editor groups failures by reason, shows five examples per page, and shows ten ready contacts per page. There is no error download. Fix rows in the editor or upload a corrected CSV. The API returns bounded results for the accepted file limit.
|
|
395
|
+
|
|
396
|
+
### 3. Start and follow the saved import
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
frontpage people import preview --csv customers.csv --file mapping.json --site YOUR_SITE --json
|
|
400
|
+
frontpage people import start --csv customers.csv --file mapping.json --request-key REQUEST_UUID --site YOUR_SITE --json
|
|
401
|
+
frontpage people import get --id IMPORT_ID --site YOUR_SITE --json
|
|
402
|
+
# After correcting a saved contact conflict, retry the same job:
|
|
403
|
+
frontpage people import retry --id IMPORT_ID --site YOUR_SITE --json
|
|
125
404
|
```
|
|
126
405
|
|
|
127
|
-
|
|
406
|
+
Choose a fresh request UUID for each new import. If a response is lost, repeat `import.start` with the same key and unchanged CSV/mapping/groups. It returns the existing job. Reusing a key for different contents returns a conflict.
|
|
407
|
+
|
|
408
|
+
The worker saves its cursor and each row’s outcome, so closing the editor does not stop the job. Use `import.get` to inspect completion and failed rows. Correcting the CSV requires a new preview and new import key. `import.retry` retries failed rows from the original saved file after you fix the underlying saved contact conflict. Completed rows keep their receipts. Imports do not send emails or grant permission through group membership.
|
|
409
|
+
|
|
410
|
+
## Email all people, a group, a stage, or selected people
|
|
128
411
|
|
|
129
|
-
|
|
412
|
+
The People button names the selected audience, such as **Email all people** or **Email Newsletter**. It opens a one-off draft in Emails. Drafting and sending stay there. A campaign does not use a template as starting content.
|
|
413
|
+
|
|
414
|
+
Save `audience.json`:
|
|
130
415
|
|
|
131
|
-
```json
|
|
132
|
-
{"audience":{"groupIds":["GROUP_ID"]}}
|
|
133
416
|
```
|
|
417
|
+
{
|
|
418
|
+
"audience": {
|
|
419
|
+
"groupIds": [
|
|
420
|
+
"GROUP_ID"
|
|
421
|
+
]
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
| Audience | JSON inside audience |
|
|
427
|
+
| ------------------ | ------------------------------------------- |
|
|
428
|
+
| All visible people | `{"filters":{}}` |
|
|
429
|
+
| Groups | `{"groupIds":["GROUP_ID"]}` |
|
|
430
|
+
| Stages | `{"stageIds":["STAGE_ID"]}` |
|
|
431
|
+
| Selected people | `{"personIds":["PERSON_ID"]}` |
|
|
432
|
+
| A filtered list | `{"filters":{"q":"Alex","followUp":"due"}}` |
|
|
134
433
|
|
|
135
|
-
|
|
434
|
+
ID lists combine as a union; filters narrow the result. Filters support `q`, `source`, `formSlug`, `followUp`, and `lastSeenDays`. An empty audience object is invalid; all people uses `{"filters":{}}` explicitly.
|
|
136
435
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
frontpage mail
|
|
141
|
-
frontpage mail campaign
|
|
142
|
-
frontpage mail campaign
|
|
436
|
+
`update.json` uses the same saved email content shape shown above, with your campaign’s subject and blocks. Create it as an email draft, not a template:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
frontpage mail email create --file update.json --name "Customer update" --site YOUR_SITE --json
|
|
440
|
+
frontpage mail campaign preview --file audience.json --site YOUR_SITE --json
|
|
441
|
+
frontpage mail campaign audience --id DRAFT_ID --file audience.json --site YOUR_SITE --json
|
|
442
|
+
# This command sends the campaign. Inspect the preview and draft first.
|
|
443
|
+
frontpage mail campaign send --id DRAFT_ID --request-key REQUEST_UUID --site YOUR_SITE --json
|
|
444
|
+
frontpage mail campaign get --id DRAFT_ID --site YOUR_SITE --json
|
|
143
445
|
```
|
|
144
446
|
|
|
145
|
-
|
|
447
|
+
Preview reports matched, eligible, no-email, suppressed, and permission-missing counts and sends nothing. Sending freezes content and eligible recipients, deduplicates email addresses, and uses the existing paced email queue. Reuse the send request key after a lost response. Check `campaign get` for queued, sent, pending, failed, and skipped work; queued does not mean delivered.
|
|
448
|
+
|
|
449
|
+
## Frontpage account sync
|
|
450
|
+
|
|
451
|
+
This admin-only feature adds Frontpage’s own account holders to the `landing` People workspace and keeps later signups in sync. It is not available to ordinary businesses. Businesses import their customer lists with CSV.
|
|
146
452
|
|
|
147
|
-
```
|
|
453
|
+
```
|
|
148
454
|
frontpage people company get --site landing --json
|
|
149
455
|
frontpage people company set --enabled true --site landing --json
|
|
150
456
|
frontpage people company set --enabled false --site landing --json
|
|
151
457
|
```
|
|
152
458
|
|
|
153
|
-
|
|
459
|
+
Sync uses saved batches and Frontpage user IDs to avoid duplicates. The backfill does not fire rules or grant email permission. Later signups can trigger group entry rules, subject to normal eligibility. Sync preserves owner edits. Pausing it leaves existing contacts saved. Supply an existing `groupId` through `--data` if you want a particular sync group.
|
|
460
|
+
|
|
461
|
+
## CLI, API, and agent reference
|
|
462
|
+
|
|
463
|
+
The editor, public API, CLI, and site agent call the same application service. Every operation below is available without opening the UI.
|
|
464
|
+
|
|
465
|
+
```
|
|
466
|
+
frontpage people workspace stages.reorder --file order.json --site YOUR_SITE --json
|
|
467
|
+
frontpage people rules create --data '{"name":"New leads","trigger":{"event":"person.added"},"conditions":null,"actions":[{"type":"set_follow_up","minutes":1440}],"enabled":false}' --site YOUR_SITE --json
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Public API
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
POST /api/v1/sites/YOUR_SITE/people/workspace
|
|
474
|
+
Authorization: Bearer YOUR_API_KEY
|
|
475
|
+
Content-Type: application/json
|
|
154
476
|
|
|
155
|
-
|
|
477
|
+
{"operation":"groups.create","label":"Leads","color":"sky","mode":"manual"}
|
|
478
|
+
```
|
|
156
479
|
|
|
157
|
-
The
|
|
480
|
+
The public workspace endpoint takes operation fields at the top level. The agent tool uses a payload object instead. Campaign operations use `POST /api/v1/sites/YOUR_SITE/mail/campaign` with `operation` set to preview, audience, send, or get. Existing contact endpoints remain available under `/api/v1/sites/YOUR_SITE/people`.
|
|
158
481
|
|
|
159
|
-
|
|
482
|
+
### Site agent
|
|
160
483
|
|
|
161
|
-
|
|
484
|
+
Ask the agent to use `get_people_docs`, then `manage_people`. Its tool binds the current site and access rights; do not pass a site ID or slug. Example membership request:
|
|
162
485
|
|
|
163
|
-
|
|
486
|
+
```
|
|
487
|
+
{
|
|
488
|
+
"operation": "membership.add",
|
|
489
|
+
"payload": {
|
|
490
|
+
"personId": "PERSON_ID",
|
|
491
|
+
"groupId": "GROUP_ID"
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
```
|
|
164
495
|
|
|
165
|
-
|
|
496
|
+
Example campaign preview:
|
|
166
497
|
|
|
167
|
-
|
|
498
|
+
```
|
|
499
|
+
{
|
|
500
|
+
"area": "campaign",
|
|
501
|
+
"operation": "preview",
|
|
502
|
+
"payload": {
|
|
503
|
+
"audience": {
|
|
504
|
+
"stageIds": [
|
|
505
|
+
"STAGE_ID"
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
```
|
|
168
511
|
|
|
169
|
-
|
|
512
|
+
The agent can create and edit saved templates and series with the existing Email tools, then attach their IDs. It can inspect runs, retry failures, update contacts, import lists, and manage group exceptions directly.
|
|
513
|
+
|
|
514
|
+
### Workspace operation reference
|
|
515
|
+
|
|
516
|
+
| Operation | Payload fields | Purpose |
|
|
517
|
+
| ------------------------------------------ | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
518
|
+
| overview | — | Active groups, stages, rules, saved emails, series, and setup state. |
|
|
519
|
+
| sources | — | Supported events and current form/product IDs. |
|
|
520
|
+
| groups.list | archived? | List groups; archived:true includes archived groups. |
|
|
521
|
+
| groups.get | id | One group with its stages. |
|
|
522
|
+
| groups.create | label, color?, mode?, conditions? | Create a group. |
|
|
523
|
+
| groups.update | id, changed fields | Edit a group without changing its ID. |
|
|
524
|
+
| groups.archive / groups.restore | id | Archive or restore a group. Related rules remain paused on restore. |
|
|
525
|
+
| groups.preview | id or conditions; personId? or cursor? | Check conditions without changing membership. |
|
|
526
|
+
| stages.create | groupId, label, color?, position?, terminal? | Create a stage in a saved group. |
|
|
527
|
+
| stages.update | id, groupId, changed fields | Edit a stage. |
|
|
528
|
+
| stages.reorder | groupId, stageIds | Save every active stage ID in order, once each. |
|
|
529
|
+
| stages.archive / stages.restore | id, groupId | Keep assignments and history while archiving or restoring. |
|
|
530
|
+
| membership.get | personId | Memberships and email eligibility. |
|
|
531
|
+
| membership.add / membership.remove | personId, groupId; reset? on add | Join or leave a manual group. |
|
|
532
|
+
| membership.move | personId, fromGroupId, groupId, reset? | Leave one manual group and join another. |
|
|
533
|
+
| membership.stage | personId, groupId, stageId | Set a stage; null clears it without leaving the group. |
|
|
534
|
+
| membership.advance | personId, groupId | Move to the next nonterminal stage. |
|
|
535
|
+
| membership.override | personId, groupId, override | Use include, exclude, or null for automatic membership. |
|
|
536
|
+
| membership.hold | personId, groupId, hold | Pause or resume automatic changes for this person/group. |
|
|
537
|
+
| rules.list | archived? | List rules; archived:true includes archived rules. |
|
|
538
|
+
| rules.get | id | Read the saved rule and version. |
|
|
539
|
+
| rules.create | name, trigger, conditions?, actions, enabled? | Create a rule. Pass enabled:false to review it first. |
|
|
540
|
+
| rules.update | id, changed fields | Save a new rule version. |
|
|
541
|
+
| rules.enable / rules.pause / rules.archive | id | Activate future events, pause, or archive. |
|
|
542
|
+
| rules.validate | rule fields or id | Validate references and automation cycles without saving. |
|
|
543
|
+
| rules.preview | rule fields or id; personId? | Evaluate a person or return validation only; sends nothing. |
|
|
544
|
+
| rules.runs | ruleId?, cursor?, limit? | Inspect saved outcomes and failed actions. |
|
|
545
|
+
| rules.retry | runId or ruleId | Retry eligible failed work; completed action receipts stay saved. |
|
|
546
|
+
| attachments.create | groupId, stageId?, emailKind, emailId, enabled?, name?, conditions? | Create an ordinary group/stage entry rule for a template or series. |
|
|
547
|
+
| person.permission | personId, allowed | Record an email-permission decision; mailing-list unsubscribe and suppression still win. |
|
|
548
|
+
| audience.preview | audience | Preview matched and eligible People for a campaign. |
|
|
549
|
+
| import.preview | csv, mapping?, headersOnly? | Validate a CSV and match existing contacts; sends nothing. |
|
|
550
|
+
| import.start | csv, mapping?, groupIds?, requestKey | Start a saved import. |
|
|
551
|
+
| import.get | id | Read the import cursor, outcomes, and errors. |
|
|
552
|
+
| import.retry | id | Retry failed rows after correcting saved identity conflicts. |
|
|
553
|
+
| company.get | — | Read the admin-only Frontpage account sync. |
|
|
554
|
+
| company.set | enabled, groupId? | Start or pause that sync in landing; platform admin only. |
|
|
555
|
+
|
|
556
|
+
## Limits, timing, and recovery
|
|
557
|
+
|
|
558
|
+
- **Worker timing:** the existing minute worker handles saved imports, account sync, condition groups, timed rules, and pending actions. A busy queue can take longer than one minute.
|
|
559
|
+
- **Temporary failures:** eligible rule actions retry with bounded backoff, up to eight attempts. Permanent errors stay visible until corrected. Completed actions retain their receipts.
|
|
560
|
+
- **Safe activation:** new UI rules and attachments start paused. Enabling applies to future events. Updating a rule changes its version and does not replay old events.
|
|
561
|
+
- **Safe membership:** cycle checks reject rules that would repeatedly add/remove or move people in a loop. Stage guards prevent late events from resetting later progress.
|
|
562
|
+
- **Safe email:** sending uses the existing monthly/hourly/site limits, platform cap, send window, provider pacing, and kill switch. Permission and suppression are checked at delivery.
|
|
563
|
+
- **Safe retries:** reuse a request key only for the same import or send. Inspect the saved job/run before starting a replacement.
|
|
564
|
+
- **Safe changes:** rename labels while retaining IDs. Archive instead of deleting records. Review paused rules when restoring groups or stages.
|
|
565
|
+
|
|
566
|
+
**Keep scope clear.**
|
|
567
|
+
|
|
568
|
+
People does not replace the raw Inbox, Store orders, or the email send ledger. It combines the contact’s history and organization. Those source tools and their existing CLI commands remain available.
|
package/dist/commands/push.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import { join, relative, sep } from "node:path";
|
|
3
3
|
import { resolveSlug } from "../site.js";
|
|
4
|
-
import { EXIT_OK, EXIT_USAGE, v1Get, v1Put } from "../http/client.js";
|
|
4
|
+
import { CliHttpError, EXIT_CONFLICT, EXIT_OK, EXIT_USAGE, v1Get, v1Put, } from "../http/client.js";
|
|
5
|
+
import { gitBlobSha } from "../git-blob-sha.js";
|
|
5
6
|
import { readState, writeState } from "../state/frontpage-state.js";
|
|
6
7
|
import { emitErr, emitOk, emitUsage, isJson } from "../runtime.js";
|
|
7
8
|
import { waitForAgentThenCompile } from "./wait-site-compile.js";
|
|
@@ -47,7 +48,9 @@ async function walkAllowlisted(cwd) {
|
|
|
47
48
|
for (const ent of entries) {
|
|
48
49
|
const child = join(abs, ent.name);
|
|
49
50
|
if (ent.isDirectory()) {
|
|
50
|
-
if (ent.name === "node_modules" ||
|
|
51
|
+
if (ent.name === "node_modules" ||
|
|
52
|
+
ent.name === ".git" ||
|
|
53
|
+
ent.name === ".frontpage")
|
|
51
54
|
continue;
|
|
52
55
|
await walk(child);
|
|
53
56
|
}
|
|
@@ -83,7 +86,9 @@ export async function runPush(args) {
|
|
|
83
86
|
const messageIdx = work.indexOf("--message");
|
|
84
87
|
const message = messageIdx >= 0
|
|
85
88
|
? work[messageIdx + 1]
|
|
86
|
-
: work
|
|
89
|
+
: work
|
|
90
|
+
.find((a) => a.startsWith("--message="))
|
|
91
|
+
?.slice("--message=".length);
|
|
87
92
|
const cwd = process.cwd();
|
|
88
93
|
const state = await readState(cwd);
|
|
89
94
|
if (!state.baseCommitSha) {
|
|
@@ -94,6 +99,7 @@ export async function runPush(args) {
|
|
|
94
99
|
return emitUsage("push", "No allowlisted files (src/, public/, content/) found in this folder.", EXIT_USAGE);
|
|
95
100
|
}
|
|
96
101
|
const listing = await v1Get(`/api/v1/sites/${encodeURIComponent(slug)}/files`);
|
|
102
|
+
const remoteByPath = new Map(listing.files.map((file) => [file.path, file.blobSha]));
|
|
97
103
|
const files = [
|
|
98
104
|
...pushDeletesForMissingLocal(paths, listing.files.map((f) => f.path)),
|
|
99
105
|
];
|
|
@@ -102,21 +108,38 @@ export async function runPush(args) {
|
|
|
102
108
|
const st = await stat(abs);
|
|
103
109
|
if (!st.isFile())
|
|
104
110
|
continue;
|
|
111
|
+
const buf = await readFile(abs);
|
|
112
|
+
if (gitBlobSha(buf) === remoteByPath.get(path))
|
|
113
|
+
continue;
|
|
105
114
|
if (isTextPath(path)) {
|
|
106
|
-
const content =
|
|
115
|
+
const content = buf.toString("utf8");
|
|
107
116
|
files.push({ path, encoding: "utf-8", content });
|
|
108
117
|
}
|
|
109
118
|
else {
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
files.push({
|
|
120
|
+
path,
|
|
121
|
+
encoding: "base64",
|
|
122
|
+
content: buf.toString("base64"),
|
|
123
|
+
});
|
|
112
124
|
}
|
|
113
125
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
files
|
|
119
|
-
|
|
126
|
+
if (!files.length && listing.baseCommitSha !== state.baseCommitSha) {
|
|
127
|
+
throw new CliHttpError("Remote tip moved since your last pull. Run frontpage pull and retry.", EXIT_CONFLICT, 409, "conflict");
|
|
128
|
+
}
|
|
129
|
+
const data = files.length
|
|
130
|
+
? await v1Put(`/api/v1/sites/${encodeURIComponent(slug)}/files`, {
|
|
131
|
+
baseCommitSha: state.baseCommitSha,
|
|
132
|
+
expectedHeadSha: state.baseCommitSha,
|
|
133
|
+
message: message || undefined,
|
|
134
|
+
files,
|
|
135
|
+
})
|
|
136
|
+
: {
|
|
137
|
+
changed: false,
|
|
138
|
+
commitSha: state.baseCommitSha,
|
|
139
|
+
baseCommitSha: state.baseCommitSha,
|
|
140
|
+
filesChanged: [],
|
|
141
|
+
warnings: [],
|
|
142
|
+
};
|
|
120
143
|
if (data.commitSha) {
|
|
121
144
|
await writeState({ ...state, slug, baseCommitSha: data.commitSha }, cwd);
|
|
122
145
|
}
|
package/dist/registry.js
CHANGED
|
@@ -461,7 +461,7 @@ export const COMMANDS = [
|
|
|
461
461
|
"frontpage people export",
|
|
462
462
|
],
|
|
463
463
|
flags: [
|
|
464
|
-
"Workspace: groups list|get|create|update|archive|restore|preview; stages create|update|archive|restore; membership get|add|remove|move|stage|advance|override|hold; rules list|get|create|update|enable|pause|archive|validate|preview|runs|retry; attachments create; import preview|start|get|retry; person permission; company get|set; audience preview; overview; sources",
|
|
464
|
+
"Workspace: groups list|get|create|update|archive|restore|preview; stages create|update|reorder|archive|restore; membership get|add|remove|move|stage|advance|override|hold; rules list|get|create|update|enable|pause|archive|validate|preview|runs|retry; attachments create; import preview|start|get|retry; person permission; company get|set; audience preview; overview; sources",
|
|
465
465
|
"All workspace operations accept --file JSON or --data JSON. Named flags: --id --label --name --group --stage --person --from-group --mode --color --position --terminal --enabled --hold --allowed --archived --reset --override include|exclude|auto --email-kind template|series --email-id --rule --run --cursor --limit --request-key. Boolean flags take true|false. Complex conditions/actions/mappings use JSON.",
|
|
466
466
|
"CSV: --csv PATH plus --file mapping.json. import start requires a stable --request-key. Retry the same import with the same key and contents.",
|
|
467
467
|
"list/export: --group GROUP_ID --stage STAGE_ID. See package PEOPLE.md for full examples and every JSON shape.",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frontpage-host",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Frontpage CLI
|
|
3
|
+
"version": "0.1.25",
|
|
4
|
+
"description": "Frontpage CLI — manage your site from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"frontpage": "dist/bin.js"
|