@xmarts/genius-setup 1.11.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmarts/genius-setup",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "One-command, self-updating onboarding for Xmarts Genius (Brain MCP + per-turn injection + automatic session/time capture + presence heartbeat for honest time-tracking 'amarre' + plan/committee→Brain enforcement + CLAUDE.md protocol + MCP fan-out: provisions the consultant's granted MCP servers into Claude Code & Claude Desktop). Secret-free onboarding via single-use setup-token exchange. Installs once; self-updates daily so new tools/skills/features arrive with zero action.",
5
5
  "bin": {
6
6
  "genius-setup": "bin/genius-setup.js"
@@ -67,6 +67,7 @@ argument shape or hit an edge case.
67
67
  | Deploy / install / upgrade a **module** | `manage_module`, `deploy_module` | ✅ — see [manage-client-modules.md](references/manage-client-modules.md) |
68
68
  | Inspect a model's schema | `get_model_schema`, `list_models` | ✅ |
69
69
  | Read / search a client's source code | `code_read`, `code_search`, `search_webapp_code` | ✅ |
70
+ | Give a **consultant a UI login** on a client | `execute_orm` (create `res.users`) | ✅ — see [provision-consultant-ui-access.md](references/provision-consultant-ui-access.md) |
70
71
  | Snapshot / restore / export the DB | `create_snapshot`, `restore_snapshot`, `export_database` | ✅ |
71
72
  | Restart Odoo | `trigger_restart` | ✅ |
72
73
  | Build a chart/dashboard | `create_echart` | ✅ *(same infra caveat as webapps)* |
@@ -0,0 +1,101 @@
1
+ # Provisioning a consultant a UI login on a client Odoo
2
+
3
+ **The need (recurring):** a Genius consultant must log into a *client's* Odoo **UI**
4
+ (`https://<client>/web/login`) to work visually — test screens, configure Studio, click
5
+ through flows. Genius manages consultants centrally (`xma.consultant` on the master), but
6
+ each client is a **separate Odoo database**; a master identity is **not** a login on the
7
+ client. There is no SSO federation — the `xma_connect` gateway is HMAC machine-to-machine,
8
+ not user identity. So the consultant needs a real `res.users` **in that client instance**.
9
+
10
+ **Do NOT share the gateway service account** (`xma_genius@<client>`) or any other shared
11
+ login. It has only `base.group_user` (headless), sharing a password kills the audit trail,
12
+ and a plaintext password in a Claude session can be indexed by the Brain. Give each
13
+ consultant their **own** audited user.
14
+
15
+ ## The pattern (scalable, sustainable, auditable)
16
+
17
+ **Per-consultant internal user in the client instance, provisioned on demand through the
18
+ Genius gateway, cloning an existing consultant's group profile, with a temp password the
19
+ consultant changes on first login.**
20
+
21
+ - **Auditable** — real per-person `res.users`; `create_uid`/`create_date`/login identify who.
22
+ - **Consistent** — clone the groups of a consultant who already works that client (no
23
+ over/under-grant guesswork). If none exists, use the client's Sales-user baseline
24
+ (`base.group_user` + the Sales "All Documents" group).
25
+ - **Central + repeatable** — one `execute_orm` through `target_client`; no per-client manual admin.
26
+ - **Least-shared-secret** — temp password, changed on first login; deep "modify anything"
27
+ work happens through the consultant's Claude Code + gateway (full ORM/code), NOT the UI login.
28
+
29
+ ### UI login ≠ programmatic access — two distinct rails
30
+ 1. **UI login** (this doc) — for *visual* work. Modest groups (mirror an existing consultant).
31
+ 2. **Programmatic** (Claude Code + `beesmart_genius` MCP + `target_client`) — for reading
32
+ code, editing modules/webapps, running ORM. This already runs with full gateway access, so
33
+ a consultant rarely needs broad UI groups. Grant the UI the minimum for visual work; escalate
34
+ one group at a time only when a specific task in the UI is blocked.
35
+
36
+ ## Runbook (≈1 minute)
37
+
38
+ Resolve the client UUID with `genius_capability_map` (its payload lists every client), then:
39
+
40
+ **1. Probe the client:** confirm reachable, the consultant has no login yet, whether SMTP
41
+ exists (usually not → no email invite → temp password), and an existing consultant to clone.
42
+ ```python
43
+ U = env['res.users'].sudo()
44
+ base_url = env['ir.config_parameter'].sudo().get_param('web.base.url')
45
+ internal = [{'id': u.id, 'login': u.login, 'name': u.name}
46
+ for u in U.search([('share','=',False),('active','=',True)])]
47
+ exists = U.search([('login','=','<consultant.email>')]).ids
48
+ result = {'base_url': base_url, 'internal': internal, 'already_there': exists}
49
+ ```
50
+ Pick the `source` user to clone (an existing consultant on this client). Odoo 19 uses
51
+ `group_ids` (NOT `groups_id`) for direct groups on `res.users`.
52
+
53
+ **2. Create the user** (clone groups + server-side temp password; suppress the reset-email
54
+ attempt that fails with no SMTP):
55
+ ```python
56
+ U = env['res.users'].sudo()
57
+ LOGIN = '<consultant.email>' # match their xmarts.com email for consistency
58
+ if U.search([('login','=',LOGIN)]):
59
+ result = {'status': 'exists'}
60
+ else:
61
+ env.cr.execute("SELECT replace(gen_random_uuid()::text,'-','')")
62
+ raw = env.cr.fetchone()[0]
63
+ pw = 'Xma-%s-%s-Ux!' % (raw[:6], raw[6:12]) # no spaces, meets complexity
64
+ src = U.browse(<source_uid>) # an existing consultant on this client
65
+ comp = src.company_id.id or env.company.id
66
+ user = U.with_context(no_reset_password=True, mail_create_nosubscribe=True).create({
67
+ 'name': '<Consultant Name>', 'login': LOGIN, 'email': LOGIN, 'password': pw,
68
+ 'company_id': comp, 'company_ids': [Command.set([comp])],
69
+ 'group_ids': [Command.set(src.group_ids.ids)],
70
+ })
71
+ result = {'status':'created','uid':user.id,'password':pw,'groups':user.group_ids.ids}
72
+ ```
73
+
74
+ **3. Verify** the hash persisted (login will work) — never read/expose the hash value:
75
+ ```python
76
+ env.cr.execute("SELECT (password IS NOT NULL AND length(password)>20) FROM res_users WHERE id=<uid>")
77
+ result = {'password_ok': bool(env.cr.fetchone()[0])}
78
+ ```
79
+
80
+ **4. Deliver securely.** URL (`<base_url>/web/login`) + login are non-secret → inline is fine.
81
+ The password is a secret: the session-capture hook redacts `password: <value>` (English
82
+ keyword + value) both client- and server-side, so present it in **that exact form** — the
83
+ owner sees it live, the Brain stores `[REDACTED]`. Also write it to `~/.genius/secrets/<client>_<consultant>_ui.txt`
84
+ (chmod 600) for reliable relay. **Mandate a change on first login** (Preferences → Account
85
+ Security) — that burns the transited temp value.
86
+
87
+ **5. Give them Claude Code too** (so they can modify anything programmatically): on the
88
+ master, `env['xma.consultant'].browse(<id>).write({'token_expiry_mode':'7d'})` then
89
+ `.action_generate_setup_package()`; read the newest `xma.consultant.setup.wizard` for that
90
+ consultant → its `npx_command`. They run it once in VSCode (Windows: PowerShell form). It
91
+ installs the MCP + hooks + skills and self-updates daily. Confirm the client is in their
92
+ `client_ids` so `target_client` routing works.
93
+
94
+ ## Notes / gotchas
95
+ - **No `signup_token` in this estate** (auth_signup is partial on xmarts.net/clients) → the
96
+ reset-link flow is unreliable; use the temp-password path. (Brain: "signup_token roto".)
97
+ - **Odoo 19:** `res.users` direct groups = `group_ids`; `all_group_ids` is computed (implied).
98
+ - **Revoke** = archive the user on the client (`delete_record` → `active=False`), reversible.
99
+ - **Don't** grant `base.group_system` (Settings/admin) by default — escalate per task only.
100
+ - This is a per-`(consultant, client)` on-demand action, not an auto-hook: not every
101
+ consultant needs UI on every client.