@xmarts/genius-setup 1.6.0 → 1.7.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/hooks/genius_capture.py +39 -8
- package/hooks/genius_inject.py +31 -7
- package/package.json +1 -1
package/hooks/genius_capture.py
CHANGED
|
@@ -251,6 +251,28 @@ def _deadletter(path):
|
|
|
251
251
|
pass
|
|
252
252
|
|
|
253
253
|
|
|
254
|
+
def _local_tz():
|
|
255
|
+
"""(iana_name_or_'', utc_offset_minutes) for THIS machine — the authoritative
|
|
256
|
+
signal for dating the work to the consultant's LOCAL day (beats IP geo, which
|
|
257
|
+
VPNs/Cloudflare defeat). The hook runs ON the consultant's machine, so it just
|
|
258
|
+
reads the machine's own timezone."""
|
|
259
|
+
iana = ""
|
|
260
|
+
try:
|
|
261
|
+
p = os.path.realpath("/etc/localtime")
|
|
262
|
+
if "/zoneinfo/" in p:
|
|
263
|
+
iana = p.split("/zoneinfo/", 1)[1]
|
|
264
|
+
elif os.environ.get("TZ"):
|
|
265
|
+
iana = os.environ.get("TZ", "")
|
|
266
|
+
except Exception:
|
|
267
|
+
iana = ""
|
|
268
|
+
off = None
|
|
269
|
+
try:
|
|
270
|
+
off = int(_dt.datetime.now().astimezone().utcoffset().total_seconds() // 60)
|
|
271
|
+
except Exception:
|
|
272
|
+
off = None
|
|
273
|
+
return iana, off
|
|
274
|
+
|
|
275
|
+
|
|
254
276
|
def main():
|
|
255
277
|
cfg = _load_cfg()
|
|
256
278
|
endpoint = cfg.get("capture_endpoint")
|
|
@@ -272,13 +294,17 @@ def main():
|
|
|
272
294
|
transcript = _redact(_read_transcript(tpath))
|
|
273
295
|
session_ref = event.get("session_id") or event.get("session_ref") or ""
|
|
274
296
|
|
|
275
|
-
#
|
|
276
|
-
#
|
|
277
|
-
#
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
297
|
+
# CONTENT-OUT is decoupled from "off-platform". ONLY an explicit pii_safe flag
|
|
298
|
+
# keeps transcript content out of the Brain (e.g. Ateneai medical/insurance).
|
|
299
|
+
# Off-platform / no-client work STILL contributes learnings — we nourish from
|
|
300
|
+
# ALL of a consultant's projects, not just registered clients (owner directive
|
|
301
|
+
# 2026-06-05, Brain #1403). Naming a tema NO LONGER suppresses content.
|
|
302
|
+
content_out = bool(cfg.get("pii_safe"))
|
|
303
|
+
# The session METADATA still carries an off-platform LABEL (no Genius client);
|
|
304
|
+
# the server derives the same. It does NOT gate transcript content.
|
|
305
|
+
is_offplatform = content_out or not cfg.get("target_client")
|
|
306
|
+
|
|
307
|
+
if transcript.strip() and not content_out:
|
|
282
308
|
payload = {
|
|
283
309
|
"transcript": transcript,
|
|
284
310
|
"session_ref": session_ref,
|
|
@@ -295,10 +321,11 @@ def main():
|
|
|
295
321
|
if session_endpoint and session_ref:
|
|
296
322
|
metrics = _session_metrics(tpath)
|
|
297
323
|
if metrics and metrics.get("active_minutes", 0) > 0:
|
|
324
|
+
tz_iana, tz_off = _local_tz()
|
|
298
325
|
meta = {
|
|
299
326
|
"session_ref": session_ref,
|
|
300
327
|
"source": "transcript_hook",
|
|
301
|
-
"is_offplatform":
|
|
328
|
+
"is_offplatform": is_offplatform,
|
|
302
329
|
"_endpoint": session_endpoint,
|
|
303
330
|
}
|
|
304
331
|
meta.update(metrics)
|
|
@@ -309,6 +336,10 @@ def main():
|
|
|
309
336
|
meta["workspace"] = ws
|
|
310
337
|
if cfg.get("topic"):
|
|
311
338
|
meta["topic"] = cfg["topic"]
|
|
339
|
+
if tz_iana:
|
|
340
|
+
meta["tz_iana"] = tz_iana
|
|
341
|
+
if tz_off is not None:
|
|
342
|
+
meta["utc_offset_minutes"] = tz_off
|
|
312
343
|
_spool(meta, prefix="meta")
|
|
313
344
|
|
|
314
345
|
# Always attempt to flush the spool (delivers this + any backlog)
|
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"
|
|
@@ -73,17 +74,22 @@ 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 ""), result.get("hook_force_epoch")
|
|
78
79
|
except Exception:
|
|
79
|
-
return ""
|
|
80
|
+
return "", None
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
def _maybe_self_update(cfg):
|
|
83
|
+
def _maybe_self_update(cfg, force_epoch=None):
|
|
83
84
|
"""Keep the consultant on the LATEST Genius package with ZERO action: at most
|
|
84
85
|
once/day, silently re-run the installer in the background. New hooks, MCP
|
|
85
86
|
tools, settings and the CLAUDE.md protocol then flow automatically — the
|
|
86
87
|
consultant never re-onboards. Detached + fully guarded; never blocks a turn.
|
|
88
|
+
|
|
89
|
+
force_epoch (from the server's inject response) is the admin "push now" lever:
|
|
90
|
+
when the server epoch is newer than the one we last acted on, we upgrade
|
|
91
|
+
IMMEDIATELY, bypassing the daily TTL — and record the epoch so we force at most
|
|
92
|
+
once per bump (never a loop).
|
|
87
93
|
"""
|
|
88
94
|
import time
|
|
89
95
|
import subprocess
|
|
@@ -91,7 +97,18 @@ def _maybe_self_update(cfg):
|
|
|
91
97
|
token = cfg.get("token")
|
|
92
98
|
if not token:
|
|
93
99
|
return
|
|
94
|
-
|
|
100
|
+
forced = False
|
|
101
|
+
if force_epoch:
|
|
102
|
+
try:
|
|
103
|
+
fe = int(force_epoch)
|
|
104
|
+
last = 0
|
|
105
|
+
if os.path.exists(FORCE_STAMP):
|
|
106
|
+
with open(FORCE_STAMP) as fh:
|
|
107
|
+
last = int((fh.read() or "0").strip() or 0)
|
|
108
|
+
forced = fe > last
|
|
109
|
+
except Exception:
|
|
110
|
+
forced = False
|
|
111
|
+
if not forced and os.path.exists(SELF_UPDATE_STAMP) and (
|
|
95
112
|
time.time() - os.path.getmtime(SELF_UPDATE_STAMP)) < SELF_UPDATE_TTL:
|
|
96
113
|
return
|
|
97
114
|
# Stamp FIRST so a failing/slow npx never causes a retry storm.
|
|
@@ -99,6 +116,9 @@ def _maybe_self_update(cfg):
|
|
|
99
116
|
os.makedirs(os.path.dirname(SELF_UPDATE_STAMP), exist_ok=True)
|
|
100
117
|
with open(SELF_UPDATE_STAMP, "w") as fh:
|
|
101
118
|
fh.write(str(int(time.time())))
|
|
119
|
+
if force_epoch:
|
|
120
|
+
with open(FORCE_STAMP, "w") as fh:
|
|
121
|
+
fh.write(str(int(force_epoch)))
|
|
102
122
|
except Exception:
|
|
103
123
|
return
|
|
104
124
|
env = dict(os.environ)
|
|
@@ -125,14 +145,18 @@ def main():
|
|
|
125
145
|
cfg = _cfg()
|
|
126
146
|
if not cfg.get("token"):
|
|
127
147
|
return # not configured -> silent no-op
|
|
128
|
-
_maybe_self_update(cfg) # background, daily, zero-action upgrades
|
|
129
148
|
event = _event()
|
|
130
149
|
name = event.get("hook_event_name") or (
|
|
131
150
|
"UserPromptSubmit" if event.get("prompt") else "SessionStart")
|
|
132
151
|
if name == "SessionStart":
|
|
152
|
+
_maybe_self_update(cfg) # TTL-only on priming
|
|
133
153
|
_emit("SessionStart", SESSION_PRIMER)
|
|
134
154
|
else:
|
|
135
|
-
|
|
155
|
+
context, force_epoch = _inject(cfg, event.get("prompt") or "")
|
|
156
|
+
# The inject call already round-tripped the server, so honour any
|
|
157
|
+
# admin-pushed force-update epoch here (bypasses the daily TTL).
|
|
158
|
+
_maybe_self_update(cfg, force_epoch)
|
|
159
|
+
_emit("UserPromptSubmit", context)
|
|
136
160
|
|
|
137
161
|
|
|
138
162
|
if __name__ == "__main__":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmarts/genius-setup",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
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"
|