@xmarts/genius-setup 1.12.0 → 1.13.1
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.
|
|
3
|
+
"version": "1.13.1",
|
|
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"
|
|
@@ -10,7 +10,11 @@ description: >
|
|
|
10
10
|
deploy a webapp to a client, can't see/manage a client's modules, or whenever the work
|
|
11
11
|
touches xma_genius, xma_connect, beesmart_genius, the Genius Brain, GEA Ecuador, or
|
|
12
12
|
deploying/controlling anything on a client instance from the central — even if the user
|
|
13
|
-
doesn't name the tool.
|
|
13
|
+
doesn't name the tool. ALSO use it the moment a consultant needs to LOG INTO a client's
|
|
14
|
+
Odoo UI / needs a login / user / password / access URL for a client instance (e.g.
|
|
15
|
+
"necesito acceso a la UI de X", "no puedo entrar a la instancia de X", "olvidé mi
|
|
16
|
+
contraseña de X", "how do I log into X") — provision it and hand back the credentials
|
|
17
|
+
on the spot. If you ever think "I don't have access to that client", read this
|
|
14
18
|
skill first: you almost certainly do.
|
|
15
19
|
---
|
|
16
20
|
|
|
@@ -78,6 +82,19 @@ argument shape or hit an edge case.
|
|
|
78
82
|
33 tools are routable to clients in total. If a tool you need isn't listed, check
|
|
79
83
|
[references/tool-catalog.md](references/tool-catalog.md) before concluding it's impossible.
|
|
80
84
|
|
|
85
|
+
## Consultant needs to log into a client's UI → hand them credentials NOW
|
|
86
|
+
|
|
87
|
+
If a consultant says any of *"necesito/quiero acceso a la UI de \<cliente\>"*, *"no puedo entrar
|
|
88
|
+
a la instancia de \<cliente\>"*, *"how do I log into \<client\>"*, or *"olvidé mi contraseña de
|
|
89
|
+
\<cliente\>"* — **provision it and give back URL + user + password on the spot. No waiting, no
|
|
90
|
+
admin hop.** Every consultant has `odoo.write odoo.execute` on their **assigned** clients, so this
|
|
91
|
+
runs in their own session via `target_client`. It is one idempotent `execute_orm` (creates on first
|
|
92
|
+
ask, resets the temp password on re-ask). The **exact one-shot snippet + how to hand back the
|
|
93
|
+
credential without leaking it to the Brain** is in
|
|
94
|
+
[references/provision-consultant-ui-access.md](references/provision-consultant-ui-access.md) — read
|
|
95
|
+
it and act. The only thing that can block you is the client not being in their assignments (then an
|
|
96
|
+
admin assigns it); everything else is self-serve.
|
|
97
|
+
|
|
81
98
|
## Always orient before you act
|
|
82
99
|
|
|
83
100
|
The Brain (`query_knowledge`) is the server-side source of truth and it remembers things this
|
|
@@ -12,6 +12,74 @@ login. It has only `base.group_user` (headless), sharing a password kills the au
|
|
|
12
12
|
and a plaintext password in a Claude session can be indexed by the Brain. Give each
|
|
13
13
|
consultant their **own** audited user.
|
|
14
14
|
|
|
15
|
+
## ⚡ Frictionless trigger — do this the moment a consultant asks
|
|
16
|
+
|
|
17
|
+
**Trigger phrases** (any consultant, any session): *"necesito/quiero acceso a la UI de \<cliente\>"*,
|
|
18
|
+
*"how do I log into \<client\>'s Odoo"*, *"no puedo entrar a la instancia de \<cliente\>"*,
|
|
19
|
+
*"olvidé mi contraseña de \<cliente\>"*. → **Provision immediately and hand back URL + user +
|
|
20
|
+
password. Do NOT make them wait or ask an admin.** Every consultant has `odoo.write odoo.execute`
|
|
21
|
+
on their **assigned** clients, so this runs in their own session through `target_client`.
|
|
22
|
+
|
|
23
|
+
**One shot (idempotent — creates on first ask, resets the temp password on re-ask):** run via
|
|
24
|
+
`execute_orm` with `target_client=<client name or UUID>`. Resolve the requesting person's real
|
|
25
|
+
name + xmarts.com email first (from `get_my_profile`, or the consultant record on the master).
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
U = env['res.users'].sudo()
|
|
29
|
+
NAME, LOGIN = '<Full Name>', '<person@xmarts.com>' # the consultant who needs access
|
|
30
|
+
env.cr.execute("SELECT replace(gen_random_uuid()::text, '-', '')")
|
|
31
|
+
raw = env.cr.fetchone()[0]
|
|
32
|
+
pw = 'Xma-%s-%s-Ux!' % (raw[:6], raw[6:12]) # temp, no spaces, meets complexity
|
|
33
|
+
user = U.search([('login', '=', LOGIN)], limit=1)
|
|
34
|
+
if user:
|
|
35
|
+
user.write({'password': pw, 'active': True}) # re-ask / forgot password -> fresh temp pw
|
|
36
|
+
status = 'reset'
|
|
37
|
+
else:
|
|
38
|
+
# Clone the RICHEST non-admin consultant peer (best working profile, never over-grant
|
|
39
|
+
# Settings-admin). Filter service accounts / admin IN PYTHON — the gateway's anti-SQL
|
|
40
|
+
# guard mangles `like`/`%` domains (a `not like 'xma_%'` term silently returns nothing).
|
|
41
|
+
# Plain loop — execute_orm forbids lambda closures. Fall back to the Sales baseline if
|
|
42
|
+
# the client has no usable peer yet.
|
|
43
|
+
peers = U.search([('share', '=', False), ('active', '=', True), ('id', '!=', 1)])
|
|
44
|
+
sysg = env.ref('base.group_system', raise_if_not_found=False)
|
|
45
|
+
peer = U.browse(); best = -1
|
|
46
|
+
for p in peers:
|
|
47
|
+
lg = p.login or ''
|
|
48
|
+
if lg == 'admin' or lg.startswith('xma_'):
|
|
49
|
+
continue # skip the admin + service accounts
|
|
50
|
+
if sysg and sysg.id in p.group_ids.ids:
|
|
51
|
+
continue # never clone an admin's groups
|
|
52
|
+
n = len(p.group_ids)
|
|
53
|
+
if n > best:
|
|
54
|
+
best = n; peer = p
|
|
55
|
+
gids = peer.group_ids.ids if peer else []
|
|
56
|
+
if not gids:
|
|
57
|
+
base = env.ref('base.group_user', raise_if_not_found=False)
|
|
58
|
+
sales = env.ref('sales_team.group_sale_salesman', raise_if_not_found=False)
|
|
59
|
+
gids = [g.id for g in (base, sales) if g]
|
|
60
|
+
comp = (peer.company_id.id if peer else False) or env.company.id
|
|
61
|
+
user = U.with_context(no_reset_password=True, mail_create_nosubscribe=True).create({
|
|
62
|
+
'name': NAME, 'login': LOGIN, 'email': LOGIN, 'password': pw,
|
|
63
|
+
'company_id': comp, 'company_ids': [Command.set([comp])],
|
|
64
|
+
'group_ids': [Command.set(gids)]})
|
|
65
|
+
status = 'created'
|
|
66
|
+
result = {'status': status, 'uid': user.id, 'login': user.login, 'password': pw,
|
|
67
|
+
'url': env['ir.config_parameter'].sudo().get_param('web.base.url') + '/web/login'}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Then **hand it back immediately**, formatting the secret so the capture hook redacts it from the
|
|
71
|
+
Brain but the consultant still sees it live:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
url: <result.url>
|
|
75
|
+
login: <result.login>
|
|
76
|
+
password: <result.password> ← temp, change it on first login (Preferences → Account Security)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Only assigned clients are reachable (`target_client` refuses unassigned). If the client is NOT in
|
|
80
|
+
their assignments, tell them to ask an admin to assign it — that is the only gate. Nothing else
|
|
81
|
+
about this should require a human hop.
|
|
82
|
+
|
|
15
83
|
## The pattern (scalable, sustainable, auditable)
|
|
16
84
|
|
|
17
85
|
**Per-consultant internal user in the client instance, provisioned on demand through the
|