@xmarts/genius-setup 1.7.0 → 1.7.2
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/hooks/genius_inject.py +58 -11
- package/package.json +1 -1
package/hooks/genius_inject.py
CHANGED
|
@@ -24,6 +24,7 @@ CFG_PATH = os.path.join(HOME, ".genius", "config.json")
|
|
|
24
24
|
TIMEOUT = 3.5
|
|
25
25
|
SELF_UPDATE_TTL = 86400 # at most once/day
|
|
26
26
|
SELF_UPDATE_STAMP = os.path.join(HOME, ".genius", ".last_selfupdate")
|
|
27
|
+
FORCE_STAMP = os.path.join(HOME, ".genius", ".last_forced_epoch")
|
|
27
28
|
|
|
28
29
|
SESSION_PRIMER = (
|
|
29
30
|
"[Xmarts Genius — Brain connected]\n"
|
|
@@ -62,7 +63,7 @@ def _inject(cfg, prompt):
|
|
|
62
63
|
base = (cfg.get("base_url") or "https://beesmart.digital").rstrip("/")
|
|
63
64
|
token = cfg.get("token")
|
|
64
65
|
if not token or not prompt.strip():
|
|
65
|
-
return ""
|
|
66
|
+
return "", None, None
|
|
66
67
|
body = json.dumps({"jsonrpc": "2.0", "method": "call", "id": 1,
|
|
67
68
|
"params": {"prompt": prompt[:4000], "limit": 4}}).encode()
|
|
68
69
|
req = urllib.request.Request(base + "/xma/genius/v1/inject", data=body)
|
|
@@ -73,17 +74,43 @@ def _inject(cfg, prompt):
|
|
|
73
74
|
try:
|
|
74
75
|
resp = urllib.request.urlopen(req, timeout=TIMEOUT)
|
|
75
76
|
data = json.loads(resp.read())
|
|
76
|
-
result = data.get("result", data)
|
|
77
|
-
return (result
|
|
77
|
+
result = data.get("result", data) or {}
|
|
78
|
+
return ((result.get("context", "") or ""),
|
|
79
|
+
result.get("hook_force_epoch"),
|
|
80
|
+
result.get("hook_pin_version"))
|
|
78
81
|
except Exception:
|
|
79
|
-
return ""
|
|
82
|
+
return "", None, None
|
|
80
83
|
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
# M6 (Phase-0 integrity gate): NEVER resolve `@latest`. An unsigned `npx @latest`
|
|
86
|
+
# self-update is a standing fleet-wide RCE channel. We install an EXACT pinned
|
|
87
|
+
# version dictated by the server (xma_genius.hook_pin_version, returned in the
|
|
88
|
+
# inject response) and otherwise fall back to this bundled known-good pin — the
|
|
89
|
+
# client never resolves the mutable `@latest` tag.
|
|
90
|
+
PINNED_FALLBACK = "1.7.2"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _pkg_spec(pin_version):
|
|
94
|
+
"""Resolve the exact package spec to install. Accepts only a plain semver pin
|
|
95
|
+
from the server; anything else falls back to the bundled PINNED_FALLBACK.
|
|
96
|
+
Never returns an `@latest` spec."""
|
|
97
|
+
import re
|
|
98
|
+
v = (pin_version or "").strip()
|
|
99
|
+
if v and re.match(r"^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.]+)?$", v):
|
|
100
|
+
return "@xmarts/genius-setup@%s" % v
|
|
101
|
+
return "@xmarts/genius-setup@%s" % PINNED_FALLBACK
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _maybe_self_update(cfg, force_epoch=None, pin_version=None):
|
|
105
|
+
"""Keep the consultant on the SERVER-PINNED Genius package with ZERO action: at most
|
|
84
106
|
once/day, silently re-run the installer in the background. New hooks, MCP
|
|
85
107
|
tools, settings and the CLAUDE.md protocol then flow automatically — the
|
|
86
108
|
consultant never re-onboards. Detached + fully guarded; never blocks a turn.
|
|
109
|
+
|
|
110
|
+
force_epoch (from the server's inject response) is the admin "push now" lever:
|
|
111
|
+
when the server epoch is newer than the one we last acted on, we upgrade
|
|
112
|
+
IMMEDIATELY, bypassing the daily TTL — and record the epoch so we force at most
|
|
113
|
+
once per bump (never a loop).
|
|
87
114
|
"""
|
|
88
115
|
import time
|
|
89
116
|
import subprocess
|
|
@@ -91,7 +118,18 @@ def _maybe_self_update(cfg):
|
|
|
91
118
|
token = cfg.get("token")
|
|
92
119
|
if not token:
|
|
93
120
|
return
|
|
94
|
-
|
|
121
|
+
forced = False
|
|
122
|
+
if force_epoch:
|
|
123
|
+
try:
|
|
124
|
+
fe = int(force_epoch)
|
|
125
|
+
last = 0
|
|
126
|
+
if os.path.exists(FORCE_STAMP):
|
|
127
|
+
with open(FORCE_STAMP) as fh:
|
|
128
|
+
last = int((fh.read() or "0").strip() or 0)
|
|
129
|
+
forced = fe > last
|
|
130
|
+
except Exception:
|
|
131
|
+
forced = False
|
|
132
|
+
if not forced and os.path.exists(SELF_UPDATE_STAMP) and (
|
|
95
133
|
time.time() - os.path.getmtime(SELF_UPDATE_STAMP)) < SELF_UPDATE_TTL:
|
|
96
134
|
return
|
|
97
135
|
# Stamp FIRST so a failing/slow npx never causes a retry storm.
|
|
@@ -99,20 +137,24 @@ def _maybe_self_update(cfg):
|
|
|
99
137
|
os.makedirs(os.path.dirname(SELF_UPDATE_STAMP), exist_ok=True)
|
|
100
138
|
with open(SELF_UPDATE_STAMP, "w") as fh:
|
|
101
139
|
fh.write(str(int(time.time())))
|
|
140
|
+
if force_epoch:
|
|
141
|
+
with open(FORCE_STAMP, "w") as fh:
|
|
142
|
+
fh.write(str(int(force_epoch)))
|
|
102
143
|
except Exception:
|
|
103
144
|
return
|
|
104
145
|
env = dict(os.environ)
|
|
105
146
|
env["GENIUS_TOKEN"] = token
|
|
106
147
|
base = cfg.get("base_url")
|
|
148
|
+
spec = _pkg_spec(pin_version) # exact pinned version — never @latest
|
|
107
149
|
devnull = open(os.devnull, "w")
|
|
108
150
|
if os.name == "nt":
|
|
109
|
-
cmd = "npx -y
|
|
151
|
+
cmd = "npx -y %s" % spec
|
|
110
152
|
if base:
|
|
111
153
|
cmd += ' --base-url "%s"' % base
|
|
112
154
|
subprocess.Popen(cmd, shell=True, env=env, stdout=devnull, stderr=devnull,
|
|
113
155
|
stdin=subprocess.DEVNULL, creationflags=0x00000008) # DETACHED_PROCESS
|
|
114
156
|
else:
|
|
115
|
-
args = ["npx", "-y",
|
|
157
|
+
args = ["npx", "-y", spec]
|
|
116
158
|
if base:
|
|
117
159
|
args += ["--base-url", base]
|
|
118
160
|
subprocess.Popen(args, env=env, stdout=devnull, stderr=devnull,
|
|
@@ -125,14 +167,19 @@ def main():
|
|
|
125
167
|
cfg = _cfg()
|
|
126
168
|
if not cfg.get("token"):
|
|
127
169
|
return # not configured -> silent no-op
|
|
128
|
-
_maybe_self_update(cfg) # background, daily, zero-action upgrades
|
|
129
170
|
event = _event()
|
|
130
171
|
name = event.get("hook_event_name") or (
|
|
131
172
|
"UserPromptSubmit" if event.get("prompt") else "SessionStart")
|
|
132
173
|
if name == "SessionStart":
|
|
174
|
+
_maybe_self_update(cfg) # TTL-only on priming
|
|
133
175
|
_emit("SessionStart", SESSION_PRIMER)
|
|
134
176
|
else:
|
|
135
|
-
|
|
177
|
+
context, force_epoch, pin_version = _inject(cfg, event.get("prompt") or "")
|
|
178
|
+
# The inject call already round-tripped the server, so honour any
|
|
179
|
+
# admin-pushed force-update epoch here (bypasses the daily TTL) and install
|
|
180
|
+
# the exact server-pinned version (never @latest).
|
|
181
|
+
_maybe_self_update(cfg, force_epoch, pin_version)
|
|
182
|
+
_emit("UserPromptSubmit", context)
|
|
136
183
|
|
|
137
184
|
|
|
138
185
|
if __name__ == "__main__":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmarts/genius-setup",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"description": "One-command, self-updating onboarding for Xmarts Genius (Brain MCP + per-turn injection + automatic session/time capture + CLAUDE.md protocol). Secret-free onboarding via single-use setup-token exchange (no Bearer in the GET-able bootstrap). 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"
|