@xmarts/genius-setup 1.17.0 → 1.18.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.
@@ -37,6 +37,13 @@ function log(msg) { process.stdout.write(msg + '\n'); }
37
37
  const BASE = (arg('base-url', 'https://beesmart.digital')).replace(/\/+$/, '');
38
38
  const CLIENT = arg('client', '');
39
39
 
40
+ // W-04: single source of the running version — package.json is always present in
41
+ // the published tarball. UA + telemetry report THIS, never a hardcoded literal
42
+ // (the fleet ran with 'XmartsGenius-Setup/1.6.0' frozen across 11 releases).
43
+ let PKG_VERSION = 'unknown';
44
+ try { PKG_VERSION = require('../package.json').version || 'unknown'; } catch (_) {}
45
+ const UA = 'XmartsGenius-Setup/' + PKG_VERSION;
46
+
40
47
  // -----------------------------------------------------------------------------
41
48
  // Token resolution (priority order):
42
49
  // 1. GENIUS_TOKEN env — an already-resolved consultant Bearer (back-compat
@@ -78,7 +85,7 @@ function exchangeSetupToken(setupToken, cb) {
78
85
  'Content-Type': 'application/json',
79
86
  'Content-Length': Buffer.byteLength(payload),
80
87
  // Cloudflare in front of beesmart.digital 403s the default Node UA.
81
- 'User-Agent': 'XmartsGenius-Setup/1.6.0',
88
+ 'User-Agent': UA,
82
89
  },
83
90
  timeout: 15000,
84
91
  }, (res) => {
@@ -152,6 +159,24 @@ function runSetup(token) {
152
159
  const guardDst = copyHook('plan_committee_guard.py');
153
160
  const presenceDst = copyHook('genius_presence.py');
154
161
 
162
+ // W-04 telemetry stamp: the inject hook reads installed_version + hooks_hash
163
+ // from config.json and reports them on every /inject, so the server-side
164
+ // fleet-skew view reflects what each device ACTUALLY runs (0/16 reported
165
+ // anything before this). Hash = sha256 over the exact hook bytes installed,
166
+ // in a fixed filename order — drift in any hook file changes it.
167
+ try {
168
+ const crypto = require('crypto');
169
+ const h = crypto.createHash('sha256');
170
+ ['genius_capture.py', 'genius_inject.py', 'plan_committee_guard.py', 'genius_presence.py']
171
+ .forEach((f) => {
172
+ try { h.update(fs.readFileSync(path.join(HOOKS_DIR, f))); } catch (_) {}
173
+ });
174
+ const c2 = readJson(cfgPath0);
175
+ c2.installed_version = PKG_VERSION;
176
+ c2.hooks_hash = h.digest('hex').slice(0, 64);
177
+ writeJson(cfgPath0, c2, true);
178
+ } catch (_) {}
179
+
155
180
  // 2) ~/.claude.json mcpServers (merge, no clobber)
156
181
  const claudeJsonPath = path.join(HOME, '.claude.json');
157
182
  const claudeJson = readJson(claudeJsonPath);
@@ -309,13 +334,18 @@ function runSetup(token) {
309
334
  let url;
310
335
  try { url = new URL(BASE + '/xma/genius/v1/mcp/secrets'); } catch (_) { return; }
311
336
  const mod = url.protocol === 'http:' ? require('http') : require('https');
312
- const payload = JSON.stringify({ jsonrpc: '2.0', method: 'call', id: 1, params: {} });
337
+ let hooksHash = '';
338
+ try { hooksHash = readJson(path.join(GENIUS_DIR, 'config.json')).hooks_hash || ''; } catch (_) {}
339
+ const payload = JSON.stringify({ jsonrpc: '2.0', method: 'call', id: 1, params: {
340
+ running_version: JSON.stringify({ 'genius-setup': PKG_VERSION }),
341
+ hooks_hash: hooksHash,
342
+ } });
313
343
  const req = mod.request({
314
344
  hostname: url.hostname, port: url.port || (url.protocol === 'http:' ? 80 : 443),
315
345
  path: url.pathname, method: 'POST',
316
346
  headers: {
317
347
  'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload),
318
- 'Authorization': 'Bearer ' + token, 'User-Agent': 'XmartsGenius-Setup/1.6.0',
348
+ 'Authorization': 'Bearer ' + token, 'User-Agent': UA,
319
349
  },
320
350
  timeout: 15000,
321
351
  }, (res) => {
@@ -9,7 +9,7 @@ prior knowledge WITHOUT asking. Editor-agnostic (terminal / VSCode / headless).
9
9
 
10
10
  Hard rules:
11
11
  - ALWAYS exit 0 (never block a turn).
12
- - Time-box the network call (<=2.5s) so injection adds no perceptible latency.
12
+ - Time-box the network call (<=3.5s, TIMEOUT below) so injection stays cheap.
13
13
  - Relevance-floored: nothing relevant -> inject nothing (zero noise).
14
14
  - Reads endpoint + bearer from ~/.genius/config.json (written by the installer);
15
15
  no secret is embedded here.
@@ -61,18 +61,35 @@ def _emit(event_name, context):
61
61
  sys.exit(0)
62
62
 
63
63
 
64
- def _inject(cfg, prompt):
64
+ def _inject(cfg, prompt, session_id=None):
65
65
  base = (cfg.get("base_url") or "https://beesmart.digital").rstrip("/")
66
66
  token = cfg.get("token")
67
67
  if not token or not prompt.strip():
68
- return "", None, None
68
+ # MUST stay a 4-tuple: main() unpacks 4 values. The historical 3-tuple
69
+ # here made every empty-prompt turn raise ValueError inside main(),
70
+ # silently losing BOTH injection and the self-update check for that turn.
71
+ return "", None, None, None
72
+ params = {"prompt": prompt[:4000], "limit": 4}
73
+ # W-04 telemetry: real Claude Code session id (server persists it as the
74
+ # hint-event session_key, enabling same-session implicit correlation) plus
75
+ # the installed package version + hooks hash (stamped into config.json by
76
+ # the installer) so the fleet-skew view knows what each device ACTUALLY runs.
77
+ if session_id:
78
+ params["session_id"] = str(session_id)[:128]
79
+ if cfg.get("installed_version"):
80
+ params["running_version"] = json.dumps(
81
+ {"genius-setup": str(cfg["installed_version"])[:32]})
82
+ if cfg.get("hooks_hash"):
83
+ params["hooks_hash"] = str(cfg["hooks_hash"])[:64]
69
84
  body = json.dumps({"jsonrpc": "2.0", "method": "call", "id": 1,
70
- "params": {"prompt": prompt[:4000], "limit": 4}}).encode()
85
+ "params": params}).encode()
71
86
  req = urllib.request.Request(base + "/xma/genius/v1/inject", data=body)
72
87
  req.add_header("Content-Type", "application/json")
73
88
  req.add_header("Authorization", "Bearer %s" % token)
74
89
  # Cloudflare in front of beesmart.digital 403s the default Python-urllib UA.
75
- req.add_header("User-Agent", "XmartsGenius-Inject/1.0")
90
+ # Dynamic UA (W-04): report the installed version, not a hardcoded 1.0.
91
+ req.add_header("User-Agent", "XmartsGenius-Inject/%s"
92
+ % (cfg.get("installed_version") or "unknown"))
76
93
  try:
77
94
  resp = urllib.request.urlopen(req, timeout=TIMEOUT)
78
95
  data = json.loads(resp.read())
@@ -90,7 +107,7 @@ def _inject(cfg, prompt):
90
107
  # version dictated by the server (xma_genius.hook_pin_version, returned in the
91
108
  # inject response) and otherwise fall back to this bundled known-good pin — the
92
109
  # client never resolves the mutable `@latest` tag.
93
- PINNED_FALLBACK = "1.17.0"
110
+ PINNED_FALLBACK = "1.18.0"
94
111
 
95
112
 
96
113
  def _pkg_spec(pin_version):
@@ -260,7 +277,8 @@ def main():
260
277
  _maybe_self_update(cfg) # TTL-only on priming
261
278
  _emit("SessionStart", SESSION_PRIMER)
262
279
  else:
263
- context, force_epoch, pin_version, mcp_epoch = _inject(cfg, event.get("prompt") or "")
280
+ context, force_epoch, pin_version, mcp_epoch = _inject(
281
+ cfg, event.get("prompt") or "", session_id=event.get("session_id"))
264
282
  # The inject call already round-tripped the server, so honour any
265
283
  # admin-pushed force-update epoch here (bypasses the daily TTL) and install
266
284
  # the exact server-pinned version (never @latest). A newer MCP manifest
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xmarts/genius-setup",
3
- "version": "1.17.0",
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.",
3
+ "version": "1.18.0",
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\u2192Brain 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"
7
7
  },
@@ -21,4 +21,4 @@
21
21
  "license": "UNLICENSED",
22
22
  "author": "Xmarts",
23
23
  "private": false
24
- }
24
+ }