@fleox-agent-crm/agentcrm 0.3.1 → 0.3.3
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 +16 -16
- package/changelog.json +22 -0
- package/dist/admin/admin-files.json +7 -7
- package/dist/admin/assets/{chartsImpl-B45AAqnr.js → chartsImpl-aBuZ1X4W.js} +1 -1
- package/dist/admin/assets/firebase-BG1GpyBB.js +2 -0
- package/dist/admin/assets/{gitJson-CAUSxl8d.js → gitJson-DzX3eZ2P.js} +1 -1
- package/dist/admin/assets/{index-ChFauO8y.js → index-DLy5RpPl.js} +5 -5
- package/dist/admin/assets/{lucide-react.min--rznIkmQ.js → lucide-react.min-BgcmukkH.js} +1 -1
- package/dist/admin/assets/{sdk-DgzLACmL.js → sdk-DtP9zqdi.js} +1 -1
- package/dist/admin/index.html +1 -1
- package/dist/agent/agentcrm.mjs +70 -12
- package/dist/agent/chunks/api-Dwb4xz1k.mjs +27 -0
- package/dist/agent/chunks/{firebase-DxGVQV9E.mjs → firebase-DWR0K1OC.mjs} +53 -27
- package/dist/agent/chunks/{hosting-Bme7LxPc.mjs → hosting-Dnn1k3cV.mjs} +1 -1
- package/dist/agent/chunks/{index-DUSdTBkz.mjs → index-CNXq2Yk_.mjs} +1 -24
- package/dist/agent/chunks/index-Dio0aBWA.mjs +6920 -0
- package/dist/agent/chunks/{index-CRkw3_iF.mjs → index-DtCezG4R.mjs} +1 -1
- package/dist/agent/chunks/{install-CbpD_7gd.mjs → install-Bp-CwDVC.mjs} +13 -4
- package/dist/agent/chunks/{sdk-whvfg786.mjs → sdk-CYgNyuoK.mjs} +132 -7005
- package/dist/version.json +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/agentcrm-admin/SKILL.md +67 -67
- package/plugin/skills/agentcrm-admin/references/records.md +5 -5
- package/plugin/skills/agentcrm-admin/references/schema.md +19 -19
- package/plugin/skills/agentcrm-extensions/SKILL.md +8 -8
- package/plugin/skills/agentcrm-extensions/references/module-recipes.md +2 -2
- package/plugin/skills/agentcrm-extensions/references/testing.md +14 -14
- package/plugin/skills/agentcrm-extensions/references/theme-recipes.md +3 -3
- package/plugin/skills/agentcrm-install/SKILL.md +13 -13
- package/plugin/skills/agentcrm-install/references/firebase.md +1 -1
- package/plugin/skills/agentcrm-install/references/github.md +1 -1
- package/plugin/skills/agentcrm-install/references/mcp.md +2 -2
- package/plugin/skills/agentcrm-install/references/plans.md +1 -1
- package/plugin/skills/agentcrm-install/references/s3.md +1 -1
- package/plugin/skills/agentcrm-manage/SKILL.md +15 -15
- package/plugin/skills/agentcrm-sales/SKILL.md +18 -18
- package/dist/admin/assets/firebase-C1x_l9P9.js +0 -2
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Use it from the browser with `npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
26
|
+
Use it from the browser with `npx -y @fleox-agent-crm/agentcrm@0.3.3 serve --json` and the folder picker.
|
|
27
27
|
|
|
28
28
|
## git-json in a private repo
|
|
29
29
|
|
|
@@ -4,7 +4,7 @@ Use TWO buckets: a PRIVATE one for the data (journal backend) and a public one f
|
|
|
4
4
|
|
|
5
5
|
## Access key (least privilege)
|
|
6
6
|
|
|
7
|
-
- **AWS**: IAM user with a policy on both buckets: `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` on `arn:aws:s3:::<bucket>/*` and `s3:ListBucket` on `arn:aws:s3:::<bucket>`. Store it: `! npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
7
|
+
- **AWS**: IAM user with a policy on both buckets: `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` on `arn:aws:s3:::<bucket>/*` and `s3:ListBucket` on `arn:aws:s3:::<bucket>`. Store it: `! npx -y @fleox-agent-crm/agentcrm@0.3.3 credentials set s3:main`, or import a profile with `--from-aws-profile <name>`.
|
|
8
8
|
- **Cloudflare R2**: R2 → Manage API tokens → Object Read & Write, scoped to the buckets. Endpoint `https://<account-id>.r2.cloudflarestorage.com`, region `auto`.
|
|
9
9
|
- **OVHcloud**: Public Cloud → Object Storage → S3 users; endpoint `https://s3.<region>.io.cloud.ovh.net`. Set `publicRead: true` on the hosting bucket.
|
|
10
10
|
- **Scaleway**: endpoint `https://s3.<region>.scw.cloud`.
|
|
@@ -4,7 +4,7 @@ description: Check, repair and update existing AgentCRM CRMs - list the CRMs thi
|
|
|
4
4
|
license: PolyForm-Noncommercial-1.0.0
|
|
5
5
|
compatibility: Requires Node.js 20+.
|
|
6
6
|
metadata:
|
|
7
|
-
version: "0.3.
|
|
7
|
+
version: "0.3.3"
|
|
8
8
|
repository: "https://github.com/fleox/agentcrm"
|
|
9
9
|
allowed-tools: Bash Read Write
|
|
10
10
|
---
|
|
@@ -15,12 +15,12 @@ This skill covers the CRMs themselves (registry, health, updates, maintenance).
|
|
|
15
15
|
|
|
16
16
|
**Language:** talk to the user in their language; keep commands and files in English.
|
|
17
17
|
|
|
18
|
-
Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
18
|
+
Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.3 <command> --json` (Node.js 20+); read `error.hint` whenever `ok` is false.
|
|
19
19
|
|
|
20
20
|
## Always start with status
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
23
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 status --json
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
- `data.tool.updateAvailable: true` -> tell the user AgentCRM `data.tool.latest` exists and offer to update the skills first (Claude Code: `/plugin marketplace update agentcrm`, then `/reload-plugins`).
|
|
@@ -32,8 +32,8 @@ npx -y @fleox-agent-crm/agentcrm@0.3.1 status --json
|
|
|
32
32
|
## Which CRM?
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
36
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
35
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 site list --json
|
|
36
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 site show <alias> --json
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
Commands take `--site <alias>`; always pass it. Without it the tool uses `AGENTCRM_SITE`, the folder's `agentcrm.json` (`{ "site": "<alias>" }`), the only site, or the default chosen with `site use <alias>` - otherwise it fails with `site-required`: ask the user which CRM. Say which CRM you are working on before changing anything.
|
|
@@ -43,12 +43,12 @@ Registry commands: `site use <alias>`, `site rename <from> <to>`, `site forget <
|
|
|
43
43
|
## Register a CRM installed elsewhere
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
46
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 site add <alias> --admin-url https://crm.acme.com/admin/ --data-credential s3:main --passphrase-credential passphrase:acme --hosting-credential s3:main --json
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
- It reads the admin's public `config.js`, opens the data (proving the credentials) and reads the hosting from the dataset.
|
|
50
50
|
- `--data-credential`: the data location key (journal), the git token (git-json) or the Firebase login.
|
|
51
|
-
- `--passphrase-credential`: encrypted journals. The user stores the passphrase from their password manager: `! npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
51
|
+
- `--passphrase-credential`: encrypted journals. The user stores the passphrase from their password manager: `! npx -y @fleox-agent-crm/agentcrm@0.3.3 credentials set passphrase:<alias>`.
|
|
52
52
|
- `--user <email>`: the CRM user the CLI acts as (default: the first admin).
|
|
53
53
|
- A CRM in a local folder without a hosted admin: `site add <alias> --runtime @config.json --data-path /path/to/folder`.
|
|
54
54
|
- `warnings` lists what is still missing; fix later with `site credentials <alias> --hosting-credential <account>` (`""` removes one).
|
|
@@ -57,7 +57,7 @@ npx -y @fleox-agent-crm/agentcrm@0.3.1 site add <alias> --admin-url https://crm.
|
|
|
57
57
|
## Health check
|
|
58
58
|
|
|
59
59
|
```bash
|
|
60
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
60
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 doctor --site <alias> --json
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
`data.checks[]` has `id`, `status` (ok / warn / fail / skip), `message` and, for problems, `fix`. Exit code 1 when a check fails. Walk the user through each fix, most important first:
|
|
@@ -75,8 +75,8 @@ Details of every check: references/doctor.md.
|
|
|
75
75
|
## Update a hosted admin
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
79
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
78
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 deploy-admin --site <alias> --json
|
|
79
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 deploy-admin --site <alias> --yes --json
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Without `--yes` it prints the plan (`needsConfirmation: true`, versions, file count): show it, ask, then re-run with `--yes`. `--all` covers every CRM with a hosting. It refuses downgrades and admins that store a newer data format. GitHub / GitLab Pages need a minute before `doctor` sees the new version.
|
|
@@ -86,8 +86,8 @@ Without `--yes` it prints the plan (`needsConfirmation: true`, versions, file co
|
|
|
86
86
|
Deleted records stay restorable until compaction. To erase them physically and shrink the journal:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
90
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
89
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 compact --site <alias> --json
|
|
90
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 compact --site <alias> --yes --json
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
Show the plan (records purged) and confirm first: it can't be undone. Run it from one place at a time.
|
|
@@ -97,12 +97,12 @@ Show the plan (records purged) and confirm first: it can't be undone. Run it fro
|
|
|
97
97
|
Scheduled / inactivity automations and lead form ingestion run when the admin is open, or from a cron. The recommended cron is a GitHub Actions workflow in a private repo: references/cron.md. Locally:
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
101
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
100
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 automations run --site <alias> --yes --json
|
|
101
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 forms ingest --site <alias> --yes --json
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
## Overview of one CRM
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
107
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 stats --site <alias> --json
|
|
108
108
|
```
|
|
@@ -4,7 +4,7 @@ description: Sales workflows on an AgentCRM - pipeline review and forecast, stal
|
|
|
4
4
|
license: PolyForm-Noncommercial-1.0.0
|
|
5
5
|
compatibility: Requires Node.js 20+. Web search / fetch tools help for enrichment.
|
|
6
6
|
metadata:
|
|
7
|
-
version: "0.3.
|
|
7
|
+
version: "0.3.3"
|
|
8
8
|
repository: "https://github.com/fleox/agentcrm"
|
|
9
9
|
allowed-tools: Bash Read Write WebSearch WebFetch
|
|
10
10
|
---
|
|
@@ -15,7 +15,7 @@ You are the user's sales assistant on top of their CRM. Read with the CLI, reaso
|
|
|
15
15
|
|
|
16
16
|
**Language:** talk to the user in their language; keep commands and files in English.
|
|
17
17
|
|
|
18
|
-
Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
18
|
+
Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.3 <command> --json` with `--site <alias>` (pick it with the `agentcrm-manage` skill). Command details are in the `agentcrm-admin` skill.
|
|
19
19
|
|
|
20
20
|
## Ground rules
|
|
21
21
|
|
|
@@ -27,10 +27,10 @@ Every command is `npx -y @fleox-agent-crm/agentcrm@0.3.1 <command> --json` with
|
|
|
27
27
|
## Pipeline review
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
31
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
32
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
33
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
30
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report pipeline --site acme --json
|
|
31
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report forecast --site acme --json
|
|
32
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report stale --site acme --json
|
|
33
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 records list --site acme --object deals --filter closeDate:before:today --filter stage:notIn:won,lost --json
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Answer with: open amount and weighted forecast (vs last review if known), deals per stage, the at-risk list (stale, close date passed, no open task, no contact), and ONE next best action per at-risk deal. Then offer to create the follow-up tasks (`tasks add … --record <dealId>`) and fix stale close dates (`records update <id> --set closeDate=…`). Playbook and next-best-action rules: references/playbooks.md.
|
|
@@ -40,9 +40,9 @@ Answer with: open amount and weighted forecast (vs last review if known), deals
|
|
|
40
40
|
For a person, company or deal:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
44
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
45
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
43
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 records get <id> --site acme --json
|
|
44
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 activities list --site acme --record <id> --related --limit 30 --json
|
|
45
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 tasks list --site acme --record <id> --status all --json
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
Brief (one page): who they are (role, company, size, industry), relationship history (last 5 interactions, open questions, promises made), open deals (stage, amount, close date), recent public news about the company (cite links), 3 goals for the meeting, questions to ask, risks. Offer to save it as a note on the record.
|
|
@@ -54,7 +54,7 @@ Read the record and its timeline (as above), then draft the email: subject + bod
|
|
|
54
54
|
## Lead qualification and enrichment
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
57
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 records list --site acme --object people --filter lifecycleStage:eq:lead --sort createdAt:desc --limit 25 --json
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
For each lead: read the record and its company; enrich from public sources (company website: size, industry, location; the person's public role); score fit (1-5) and intent from the timeline; recommend a lifecycle stage and an owner. Present a table, then apply approved changes with `records update` and log the sources with `notes add`. Details: references/enrichment.md.
|
|
@@ -62,8 +62,8 @@ For each lead: read the record and its company; enrich from public sources (comp
|
|
|
62
62
|
## Clean duplicates
|
|
63
63
|
|
|
64
64
|
```bash
|
|
65
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
66
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
65
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 records duplicates --site acme --object people --json
|
|
66
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 records duplicates --site acme --object companies --json
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
For each group, pick the primary (most complete, most activities), show what the merge keeps, then `records merge <primaryId> <dupId>...` (plan, then `--yes`) group by group.
|
|
@@ -71,18 +71,18 @@ For each group, pick the primary (most complete, most activities), show what the
|
|
|
71
71
|
## Trade show / event lead lists
|
|
72
72
|
|
|
73
73
|
1. Look at the file (headers, a few rows) and map columns to people fields; put the event name in `leadSource` (add the option if missing) and `tags`.
|
|
74
|
-
2. Dry run: `npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
74
|
+
2. Dry run: `npx -y @fleox-agent-crm/agentcrm@0.3.3 import csv leads.csv --site acme --object people --map "…" --json`. Report new vs existing contacts and errors.
|
|
75
75
|
3. After approval: the same with `--yes`.
|
|
76
76
|
4. Offer the follow-up: a task per hot lead for its owner, or a note on each record with the booth conversation if the file has comments.
|
|
77
77
|
|
|
78
78
|
## Weekly sales report
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
82
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
83
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
84
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
85
|
-
npx -y @fleox-agent-crm/agentcrm@0.3.
|
|
81
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report won-lost --site acme --months 3 --json
|
|
82
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report activity --site acme --days 7 --json
|
|
83
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report pipeline --site acme --json
|
|
84
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 report conversion --site acme --json
|
|
85
|
+
npx -y @fleox-agent-crm/agentcrm@0.3.3 tasks list --site acme --overdue --json
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
Template and what to compute: references/weekly-report.md.
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./sdk-DgzLACmL.js","./index-ChFauO8y.js","./index-TcjySLVg.css","./index.esm-Dum0J07z.js","./index.esm2017-EOzKbpUS.js","./index.esm-CtEQZRb1.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{c as Y,w as Z,_ as ee,C as L,e as te,p as z,n as ie,r as ne,F as K,a as se}from"./index-ChFauO8y.js";const oe=Symbol("deleteField");function G(e){return(e==null?void 0:e.code)??""}function _(e){const n=G(e);return n==="permission-denied"||n==="firestore/permission-denied"}const re=500;function ce(e,n){const s=[];for(let t=0;t<e.length;t+=n)s.push(e.slice(t,t+n));return s}const j="invites",F="extensionChunks",x=3e5,ae=["admin","member","viewer"];class N extends Error{constructor(n,s,t){super(s),this.reason=n,this.email=t,this.name="FirebaseAccessError"}}function be(e){return e.kind==="firebase"&&typeof e.accessError=="function"}function W(e){const{id:n,...s}=z(e);return s}function le(e){return ie({...e.data,id:e.id})}function T(e){return typeof e=="string"&&ae.includes(e)}function E(e){return typeof e=="string"?e.trim().toLowerCase():""}function H(e){return!!e&&!e.authUid&&!!E(e.email)}function ue(e,n,s){const t=new Map;for(const i of e){if(i.collection!=="users")continue;const c=i.op==="set"?i.doc.id:i.id,a=n(c),p=s.users.get(c),h=H(a)?E(a.email):"",m=H(p)?E(p.email):"";h&&h!==m&&t.set(h,{type:"delete",collection:j,id:h}),m&&p&&(p.disabled?t.set(m,{type:"delete",collection:j,id:m}):t.set(m,{type:"set",collection:j,id:m,data:{role:T(p.role)?p.role:"member",userId:c,email:m}}))}return[...t.values()]}const B=(e,n,s)=>`${e}__${n}__${s}`;function X(e){const n=(e==null?void 0:e.files)??{},s={};for(const[t,i]of Object.entries(n))typeof i=="string"&&(s[t]=Math.ceil(i.length/x));return s}function fe(e,n){const s=e.files??{},t=Object.values(s).reduce((p,h)=>p+(typeof h=="string"?h.length:0),0),i=[],c={},a=W(e);if(t>x){for(const[p,h]of Object.entries(s)){if(typeof h!="string")continue;const m=Math.ceil(h.length/x);c[p]=m;for(let y=0;y<m;y++)i.push({type:"set",collection:F,id:B(e.id,p,y),data:{ext:e.id,file:p,index:y,data:h.slice(y*x,(y+1)*x)}})}a.files={},a.chunks=c}for(const[p,h]of Object.entries(X(n)))for(let m=c[p]??0;m<h;m++)i.push({type:"delete",collection:F,id:B(e.id,p,m)});return i.unshift({type:"set",collection:"extensions",id:e.id,data:a}),i}function de(e,n){const s=new Map;for(const t of n){const i=String(t.data.ext??"");s.set(i,[...s.get(i)??[],t])}return e.map(t=>{const i=t.data.chunks;if(!i||typeof i!="object")return t;const c={},a=s.get(t.id)??[];for(const[m,y]of Object.entries(i)){const C=a.filter(O=>O.data.file===m).sort((O,D)=>Number(O.data.index)-Number(D.data.index));C.length===y&&(c[m]=C.map(O=>String(O.data.data??"")).join(""))}const{chunks:p,...h}=t.data;return{id:t.id,data:{...h,files:c}}})}function pe(e,n){const s=[];for(const t of e)if(t.op==="set"&&t.collection==="extensions")s.push(...fe(t.doc,n.extensions.get(t.doc.id)));else if(t.op==="set")s.push({type:"set",collection:t.collection,id:t.doc.id,data:W(t.doc)});else if(t.op==="delete"){if(s.push({type:"delete",collection:t.collection,id:t.id}),t.collection==="extensions")for(const[i,c]of Object.entries(X(n.extensions.get(t.id))))for(let a=0;a<c;a++)s.push({type:"delete",collection:F,id:B(t.id,i,a)})}else{if(!n[t.collection].has(t.id))continue;const i={};for(const[c,a]of Object.entries(t.fields))c==="id"||c.startsWith("id.")||(i[c]=a===void 0?oe:z(a));Object.keys(i).length&&s.push({type:"update",collection:t.collection,id:t.id,data:i})}return s}const he="files",q="Firebase Storage isn't enabled for this project: enable it in the Firebase console (Build → Storage), add `storageBucket` to the web config and deploy the storage rules (docs/files.md).";function me(e){const n=()=>{if(!e.storage)throw new K(q);return e.storage},s=i=>(se(i),`${he}/${i}`),t=async i=>{try{return await i()}catch(c){const a=G(c);throw a==="storage/unknown"||a==="storage/bucket-not-found"||a==="storage/no-default-bucket"?new K(q):c}};return{kind:"firebase",put:(i,c,a)=>t(()=>n().put(s(i),c,a==null?void 0:a.mime)),async putMany(i){for(const c of i)await t(()=>{var a;return n().put(s(c.id),c.bytes,(a=c.meta)==null?void 0:a.mime)})},get:i=>t(()=>n().get(s(i))),delete:i=>t(()=>n().delete(s(i)))}}function ge(e){const n=e.api,s=e.requireVerifiedEmail!==!1,t=e.now??Date.now;let i=null,c=null,a=null,p=new Map,h=!1;const m=n.auth.onChange(o=>{(!o||o.uid!==(c==null?void 0:c.uid))&&(i=null,c=null)});function y(o,r){const f=te();for(const d of L){const g=d==="extensions"?de(o.get(d)??[],r):o.get(d)??[];for(const l of g)f[d].set(l.id,le(l))}p=new Map(f.users);for(const d of[...f.users.values()])typeof d.replaces=="string"&&d.replaces!==d.id&&f.users.delete(d.replaces);return f}async function C(o){try{return await n.listCollection(o)}catch(r){if(_(r))return[];throw r}}async function O(o){const r=E(o.email);let f=null;try{f=await n.getDoc("users",o.uid)}catch(u){if(!_(u))throw u}if(f)return T(f.role)?f.role:"viewer";if(s&&!o.emailVerified)throw new N("unverified","Verify your email address to continue.",r);const d={email:r,name:o.displayName||r.split("@")[0]||"User",authUid:o.uid,createdAt:t(),updatedAt:t(),...o.photoURL?{avatarUrl:o.photoURL}:{}},g=(u,b={})=>n.commit([{type:"set",collection:"users",id:o.uid,data:{...d,...b,role:u}}]);try{return await g("admin"),"admin"}catch(u){if(!_(u))throw u}let l=null;try{l=r?await n.getDoc(j,r):null}catch(u){if(!_(u))throw u}const w=l&&T(l.role)?l.role:"member";try{return await g(w,l&&typeof l.userId=="string"?{replaces:l.userId}:{}),w}catch(u){if(!_(u))throw u}throw new N("notInvited","This account has not been invited to this CRM. Ask an admin to add your email in Settings → Users.",r)}async function D(){const o=await n.auth.currentUser();if(!o)return a=null,null;try{const r=await O(o);return a=null,{uid:o.uid,email:E(o.email),displayName:o.displayName||E(o.email),role:r}}catch(r){return a=r instanceof N?r:new N("error",r.message,E(o.email)),null}}async function J(o,r,f){const d=pe(o,r);(c==null?void 0:c.role)==="admin"&&d.push(...ue(o,g=>r.users.get(g)??p.get(g),f));for(const g of ce(d,re))await n.commit(g);for(const g of o){if(g.collection!=="users")continue;const l=g.op==="set"?g.doc.id:g.id,w=f.users.get(l);w?p.set(l,w):p.delete(l)}}const S=Y({kind:"firebase",async load(){const o=new Map;let r=[];await Promise.all([...L.map(async d=>o.set(d,await C(d))),C(F).then(d=>r=d)]);const f=y(o,r);return P(),f},persist:(o,r,f)=>J(o,r,f),watch(o){const r=new Map;let f=null;const d=()=>{r.size<L.length||f===null||(o(y(r,f)),P())},g=L.map(l=>n.watchCollection(l,w=>{r.set(l,w),d()},w=>{_(w)?r.set(l,[]):console.warn(`[firebase] live updates of ${l} stopped:`,w)}));return g.push(n.watchCollection(F,l=>{f=l,d()},l=>{_(l)?f=[]:console.warn("[firebase] live updates of extension chunks stopped:",l)})),()=>g.forEach(l=>l())},identity(){return i||(i=D().then(o=>(c=o,o||(i=null),o))),i},close(){m()}});function P(){if((c==null?void 0:c.role)!=="admin"||h)return;const o=[...p.values()].filter(r=>typeof r.replaces=="string");o.length&&(h=!0,setTimeout(()=>{Q(o).finally(()=>h=!1)},0))}async function Q(o){var l;const r=[],f=[],d=S.list("objects"),g=new Map(d.map(w=>[w.id,(w.fields??[]).filter(u=>u.type==="user").map(u=>u.id)]));for(const w of o){const u=w.replaces,b=w.id,k=p.get(u);for(const v of S.list("records")){const I={};v.ownerId===u&&(I.ownerId=b),v.createdBy===u&&(I.createdBy=b);for(const M of g.get(v.object)??[]){const A=(l=v.fields)==null?void 0:l[M];A===u?I[`fields.${M}`]=b:Array.isArray(A)&&A.includes(u)&&(I[`fields.${M}`]=A.map(V=>V===u?b:V))}Object.keys(I).length&&r.push({op:"patch",collection:"records",id:v.id,fields:I})}for(const v of S.list("activities"))v.assigneeId===u&&r.push({op:"patch",collection:"activities",id:v.id,fields:{assigneeId:b}});const R=S.get("settings","general");(R==null?void 0:R.ownerUserId)===u&&r.push({op:"patch",collection:"settings",id:"general",fields:{ownerUserId:b}}),k&&(r.push({op:"delete",collection:"users",id:u}),E(k.email)&&f.push({type:"delete",collection:j,id:E(k.email)})),r.push({op:"patch",collection:"users",id:b,fields:{replaces:void 0,...k&&k.role!==w.role&&T(k.role)?{role:k.role}:{}}})}try{await S.write(r,{message:"Link invited users"}),f.length&&await n.commit(f)}catch(w){console.warn("[firebase] could not link invited users:",w)}}return Z(S,me(n)),Object.assign(S,{api:n,accessError:()=>a,resetSession(){i=null,c=null,a=null}})}const $=new Map;function we(e){const n=e.firebase.projectId??"default";let s=$.get(n);return s||(s=ee(()=>import("./sdk-DgzLACmL.js"),__vite__mapDeps([0,1,2,3,4,5]),import.meta.url).then(t=>t.createSdkFirebaseApi(e.firebase)),$.set(n,s),s.catch(()=>$.delete(n))),s}let U=null;ne("firebase",{async signOut(){await(U==null?void 0:U.auth.signOut())}});async function Ee(e,n={}){var i;const s=e;if(!((i=s.firebase)!=null&&i.apiKey)||!s.firebase.projectId)throw new Error("The Firebase web config (apiKey, projectId) is missing.");const t=await we(s);return U=t,ge({api:t})}export{oe as DELETE_FIELD,F as EXTENSION_CHUNKS_COLLECTION,x as EXTENSION_CHUNK_CHARS,re as FIRESTORE_BATCH_LIMIT,N as FirebaseAccessError,j as INVITES_COLLECTION,he as STORAGE_FILES_DIR,q as STORAGE_NOT_ENABLED,de as assembleExtensionDocs,ce as chunk,Ee as createFirebaseStore,ge as createFirebaseStoreWith,E as emailKey,G as errorCode,we as firebaseApiFor,me as firebaseFileStore,ue as inviteOps,be as isFirebaseStore,_ as isPermissionDenied,pe as mutationOps};
|