@xmarts/genius-setup 1.17.0 → 1.18.1-canary.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,41 @@ 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
+ else:
83
+ # G6 (1.18.1-canary): a legacy config without installed_version used to
84
+ # keep the device SILENT on the skew view — silence is exactly how 0/16
85
+ # hid for a month (V17). Report a sentinel instead: visible skew makes
86
+ # the fleet converge; the next installer run stamps the real version.
87
+ params["running_version"] = json.dumps({"genius-setup": "pre-1.18.1"})
88
+ if cfg.get("hooks_hash"):
89
+ params["hooks_hash"] = str(cfg["hooks_hash"])[:64]
69
90
  body = json.dumps({"jsonrpc": "2.0", "method": "call", "id": 1,
70
- "params": {"prompt": prompt[:4000], "limit": 4}}).encode()
91
+ "params": params}).encode()
71
92
  req = urllib.request.Request(base + "/xma/genius/v1/inject", data=body)
72
93
  req.add_header("Content-Type", "application/json")
73
94
  req.add_header("Authorization", "Bearer %s" % token)
74
95
  # Cloudflare in front of beesmart.digital 403s the default Python-urllib UA.
75
- req.add_header("User-Agent", "XmartsGenius-Inject/1.0")
96
+ # Dynamic UA (W-04): report the installed version, not a hardcoded 1.0.
97
+ req.add_header("User-Agent", "XmartsGenius-Inject/%s"
98
+ % (cfg.get("installed_version") or "unknown"))
76
99
  try:
77
100
  resp = urllib.request.urlopen(req, timeout=TIMEOUT)
78
101
  data = json.loads(resp.read())
@@ -90,7 +113,7 @@ def _inject(cfg, prompt):
90
113
  # version dictated by the server (xma_genius.hook_pin_version, returned in the
91
114
  # inject response) and otherwise fall back to this bundled known-good pin — the
92
115
  # client never resolves the mutable `@latest` tag.
93
- PINNED_FALLBACK = "1.17.0"
116
+ PINNED_FALLBACK = "1.18.0"
94
117
 
95
118
 
96
119
  def _pkg_spec(pin_version):
@@ -260,7 +283,8 @@ def main():
260
283
  _maybe_self_update(cfg) # TTL-only on priming
261
284
  _emit("SessionStart", SESSION_PRIMER)
262
285
  else:
263
- context, force_epoch, pin_version, mcp_epoch = _inject(cfg, event.get("prompt") or "")
286
+ context, force_epoch, pin_version, mcp_epoch = _inject(
287
+ cfg, event.get("prompt") or "", session_id=event.get("session_id"))
264
288
  # The inject call already round-tripped the server, so honour any
265
289
  # admin-pushed force-update epoch here (bypasses the daily TTL) and install
266
290
  # the exact server-pinned version (never @latest). A newer MCP manifest
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmarts/genius-setup",
3
- "version": "1.17.0",
3
+ "version": "1.18.1-canary.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"