@rotifer/playground 0.22.1 → 0.23.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/CHANGELOG.md +23 -0
- package/README.md +1 -1
- package/dist/cloud/invocation.d.ts +29 -0
- package/dist/cloud/invocation.d.ts.map +1 -1
- package/dist/cloud/invocation.js +73 -7
- package/dist/cloud/invocation.js.map +1 -1
- package/dist/commands/agent-run.d.ts.map +1 -1
- package/dist/commands/agent-run.js +19 -1
- package/dist/commands/agent-run.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +24 -3
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/telemetry.d.ts +3 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +93 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/telemetry/config.d.ts +45 -0
- package/dist/telemetry/config.d.ts.map +1 -0
- package/dist/telemetry/config.js +147 -0
- package/dist/telemetry/config.js.map +1 -0
- package/dist/telemetry/consent.d.ts +28 -0
- package/dist/telemetry/consent.d.ts.map +1 -0
- package/dist/telemetry/consent.js +43 -0
- package/dist/telemetry/consent.js.map +1 -0
- package/dist/telemetry/heartbeat.d.ts +20 -0
- package/dist/telemetry/heartbeat.d.ts.map +1 -0
- package/dist/telemetry/heartbeat.js +180 -0
- package/dist/telemetry/heartbeat.js.map +1 -0
- package/dist/utils/run-logger.d.ts.map +1 -1
- package/dist/utils/run-logger.js +12 -0
- package/dist/utils/run-logger.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.telemetryCommand = void 0;
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const display = __importStar(require("../utils/display.js"));
|
|
39
|
+
const palette_js_1 = require("../utils/palette.js");
|
|
40
|
+
const config_js_1 = require("../telemetry/config.js");
|
|
41
|
+
function describeDecision(d) {
|
|
42
|
+
switch (d) {
|
|
43
|
+
case "off-env":
|
|
44
|
+
return "off — DO_NOT_TRACK or ROTIFER_TELEMETRY in this shell";
|
|
45
|
+
case "on-env":
|
|
46
|
+
return "on — ROTIFER_TELEMETRY in this shell";
|
|
47
|
+
case "off-stored":
|
|
48
|
+
return "off — your stored choice (rotifer telemetry off)";
|
|
49
|
+
case "on-stored":
|
|
50
|
+
return "on — your stored choice (rotifer telemetry on)";
|
|
51
|
+
case "on-default":
|
|
52
|
+
return "on — default (ADR-329); nobody has chosen otherwise yet";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.telemetryCommand = new commander_1.Command("telemetry")
|
|
56
|
+
.description("Show or change the anonymous usage heartbeat")
|
|
57
|
+
.action(() => {
|
|
58
|
+
// `rotifer telemetry` with no subcommand — same shape as `codegraph
|
|
59
|
+
// telemetry` with no argument, which this project measured its
|
|
60
|
+
// telemetry design against (ADR-329). Aliases `status`.
|
|
61
|
+
reportStatus();
|
|
62
|
+
})
|
|
63
|
+
.addCommand(new commander_1.Command("status")
|
|
64
|
+
.description("Show the current heartbeat state and why")
|
|
65
|
+
.action(() => reportStatus()))
|
|
66
|
+
.addCommand(new commander_1.Command("on")
|
|
67
|
+
.description("Turn the anonymous heartbeat on")
|
|
68
|
+
.action(() => {
|
|
69
|
+
const config = (0, config_js_1.setHeartbeatEnabled)(true);
|
|
70
|
+
display.success("Anonymous usage heartbeat: on");
|
|
71
|
+
display.kv("Machine ID", config.machine_id);
|
|
72
|
+
display.hint("No code, no identity — just \"this machine ran something today\".");
|
|
73
|
+
}))
|
|
74
|
+
.addCommand(new commander_1.Command("off")
|
|
75
|
+
.description("Turn the anonymous heartbeat off")
|
|
76
|
+
.action(() => {
|
|
77
|
+
(0, config_js_1.setHeartbeatEnabled)(false);
|
|
78
|
+
display.success("Anonymous usage heartbeat: off");
|
|
79
|
+
display.hint("Nothing is sent — not even an opted-out notice.");
|
|
80
|
+
}));
|
|
81
|
+
function reportStatus() {
|
|
82
|
+
const config = (0, config_js_1.loadOrInitHeartbeatConfig)();
|
|
83
|
+
const decision = (0, config_js_1.resolveHeartbeatDecision)(config);
|
|
84
|
+
const isEnabled = (0, config_js_1.heartbeatDecisionEnabled)(decision);
|
|
85
|
+
display.header("Telemetry");
|
|
86
|
+
display.kv("Anonymous heartbeat", isEnabled ? palette_js_1.c.success("on") : palette_js_1.c.muted("off"));
|
|
87
|
+
display.kv("Reason", describeDecision(decision));
|
|
88
|
+
display.kv("Machine ID", config.machine_id);
|
|
89
|
+
console.log();
|
|
90
|
+
display.hint("Details: https://rotifer.dev/telemetry");
|
|
91
|
+
display.hint("`rotifer telemetry off` / ROTIFER_TELEMETRY=0 / DO_NOT_TRACK=1 all disable it.");
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=telemetry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/commands/telemetry.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAoC;AACpC,6DAA+C;AAC/C,oDAAwC;AACxC,sDAMgC;AAEhC,SAAS,gBAAgB,CAAC,CAAoB;IAC5C,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,SAAS;YACZ,OAAO,uDAAuD,CAAC;QACjE,KAAK,QAAQ;YACX,OAAO,sCAAsC,CAAC;QAChD,KAAK,YAAY;YACf,OAAO,kDAAkD,CAAC;QAC5D,KAAK,WAAW;YACd,OAAO,gDAAgD,CAAC;QAC1D,KAAK,YAAY;YACf,OAAO,yDAAyD,CAAC;IACrE,CAAC;AACH,CAAC;AAEY,QAAA,gBAAgB,GAAG,IAAI,mBAAO,CAAC,WAAW,CAAC;KACrD,WAAW,CAAC,8CAA8C,CAAC;KAC3D,MAAM,CAAC,GAAG,EAAE;IACX,oEAAoE;IACpE,+DAA+D;IAC/D,wDAAwD;IACxD,YAAY,EAAE,CAAC;AACjB,CAAC,CAAC;KACD,UAAU,CACT,IAAI,mBAAO,CAAC,QAAQ,CAAC;KAClB,WAAW,CAAC,0CAA0C,CAAC;KACvD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,EAAE,CAAC,CAChC;KACA,UAAU,CACT,IAAI,mBAAO,CAAC,IAAI,CAAC;KACd,WAAW,CAAC,iCAAiC,CAAC;KAC9C,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,MAAM,GAAG,IAAA,+BAAmB,EAAC,IAAI,CAAC,CAAC;IACzC,OAAO,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;AACpF,CAAC,CAAC,CACL;KACA,UAAU,CACT,IAAI,mBAAO,CAAC,KAAK,CAAC;KACf,WAAW,CAAC,kCAAkC,CAAC;KAC/C,MAAM,CAAC,GAAG,EAAE;IACX,IAAA,+BAAmB,EAAC,KAAK,CAAC,CAAC;IAC3B,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;AAClE,CAAC,CAAC,CACL,CAAC;AAEJ,SAAS,YAAY;IACnB,MAAM,MAAM,GAAG,IAAA,qCAAyB,GAAE,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAA,oCAAwB,EAAC,MAAM,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAA,oCAAwB,EAAC,QAAQ,CAAC,CAAC;IAErD,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,qBAAqB,EAAE,SAAS,CAAC,CAAC,CAAC,cAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,gFAAgF,CAAC,CAAC;AACjG,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ const whoami_js_1 = require("./commands/whoami.js");
|
|
|
33
33
|
const stats_js_1 = require("./commands/stats.js");
|
|
34
34
|
const compare_js_1 = require("./commands/compare.js");
|
|
35
35
|
const network_js_1 = require("./commands/network.js");
|
|
36
|
+
const telemetry_js_1 = require("./commands/telemetry.js");
|
|
36
37
|
const vg_js_1 = require("./commands/vg.js");
|
|
37
38
|
const hello_js_1 = require("./commands/hello.js");
|
|
38
39
|
const self_update_js_1 = require("./commands/self-update.js");
|
|
@@ -100,6 +101,7 @@ agent.addCommand(agent_list_js_1.agentListCommand);
|
|
|
100
101
|
agent.addCommand(agent_run_js_1.agentRunCommand);
|
|
101
102
|
program.addCommand(hello_js_1.helloCommand);
|
|
102
103
|
program.addCommand(network_js_1.networkCommand);
|
|
104
|
+
program.addCommand(telemetry_js_1.telemetryCommand);
|
|
103
105
|
program.addCommand(vg_js_1.vgCommand);
|
|
104
106
|
program.addCommand(self_update_js_1.selfUpdateCommand);
|
|
105
107
|
program.addCommand(user_config_js_1.userConfigCommand);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,yCAA0C;AAC1C,qCAAuC;AACvC,yCAAiC;AACjC,gDAAiD;AACjD,gDAAiD;AACjD,gDAAiD;AACjD,gDAAiD;AACjD,sDAAuD;AACvD,oDAAqD;AACrD,gEAAgE;AAChE,4DAA4D;AAC5D,8DAA8D;AAC9D,8DAA8D;AAC9D,gEAAgE;AAChE,4DAA4D;AAC5D,0DAA0D;AAC1D,kDAAmD;AACnD,oDAAqD;AACrD,sDAAuD;AACvD,0DAA6E;AAC7E,oDAAqD;AACrD,sDAAuD;AACvD,wDAA2E;AAC3E,4DAA6D;AAC7D,gDAAiD;AACjD,gDAAiD;AACjD,8CAA+C;AAC/C,wDAAyD;AACzD,oDAAqD;AACrD,kDAAmD;AACnD,sDAAuD;AACvD,sDAAuD;AACvD,4CAA6C;AAC7C,kDAAmD;AACnD,8DAA8D;AAC9D,8DAA8D;AAC9D,sDAAsD;AACtD,6DAAkG;AAClG,2DAAwD;AACxD,mDAAoG;AAEpG,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAErF,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CACV,uEAAuE,CACxE;KACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;KACpB,MAAM,CAAC,QAAQ,EAAE,wCAAwC,CAAC;KAC1D,MAAM,CAAC,SAAS,EAAE,yCAAyC,CAAC;KAC5D,WAAW,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAA,mBAAM,EAAC,GAAG,CAAC,OAAO,CAAC,CAAC;KAChD,aAAa,CAAC;IACb,UAAU,CAAC,GAAY,EAAE,MAAY;QACnC,IAAI,GAAG,KAAK,OAAO;YAAE,OAAO,IAAA,iCAAoB,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC9D,OAAO,IAAA,8BAAiB,EAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACxC,CAAC;CACF,CAAC,CAAC;AAEL,OAAO,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;IAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,IAAA,0BAAa,EAAC,MAAM,CAAC,CAAC;IACxB,CAAC;SAAM,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,IAAA,0BAAa,EAAC,OAAO,CAAC,CAAC;IACzB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,2BAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAElC,OAAO,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,2BAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,+BAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,+BAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,2BAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,8BAAgB,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,6BAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,iCAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,qBAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,mBAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,6BAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,yBAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,2BAAc,CAAC,CAAC;AAEnC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,4BAA4B,CAAC,CAAC;AACjF,KAAK,CAAC,UAAU,CAAC,oCAAkB,CAAC,CAAC;AACrC,KAAK,CAAC,UAAU,CAAC,gCAAgB,CAAC,CAAC;AACnC,KAAK,CAAC,UAAU,CAAC,kCAAiB,CAAC,CAAC;AACpC,KAAK,CAAC,UAAU,CAAC,kCAAiB,CAAC,CAAC;AAEpC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC;AAC/E,KAAK,CAAC,UAAU,CAAC,oCAAkB,CAAC,CAAC;AACrC,KAAK,CAAC,UAAU,CAAC,gCAAgB,CAAC,CAAC;AACnC,KAAK,CAAC,UAAU,CAAC,8BAAe,CAAC,CAAC;AAElC,OAAO,CAAC,UAAU,CAAC,uBAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,2BAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,iBAAS,CAAC,CAAC;AAC9B,OAAO,CAAC,UAAU,CAAC,kCAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,kCAAiB,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,0BAAa,CAAC,CAAC;AAElC,uDAAuD;AACvD,8DAA8D;AAC9D,4DAA4D;AAC5D,CAAC,SAAS,aAAa,CAAC,GAAY;IAClC,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC/B,GAAG,CAAC,aAAa,CAAC;YAChB,UAAU,CAAC,IAAa,EAAE,MAAY;gBACpC,OAAO,IAAA,iCAAoB,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC5C,CAAC;SACF,CAAC,CAAC;QACH,GAAG,CAAC,eAAe,CAAC;YAClB,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK;YACpD,eAAe,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK;SACrD,CAAC,CAAC;QACH,aAAa,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AAEZ,MAAM,UAAU,GAAG,IAAA,+BAAc,GAAE,CAAC;AACpC,IAAI,UAAU,CAAC,cAAc,CAAC,KAAK,KAAK,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,IAAA,gCAAc,EAAC,qBAAqB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAClE,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAA,yCAAuB,EAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC,CAAC;IACnF,CAAC;IACD,IAAA,gCAAc,EAAC,qBAAqB,EAAE,GAAG,CAAC,OAAO,CAAC;SAC/C,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACb,IAAI,IAAI,IAAI,CAAC,MAAM;YAAE,IAAA,yCAAuB,EAAC,IAAI,EAAE,qBAAqB,CAAC,CAAC;IAC5E,CAAC,CAAC;SACD,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACrB,CAAC;AAED,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type ConsentSource = "installer" | "cli" | "default-notice";
|
|
2
|
+
export interface HeartbeatConfig {
|
|
3
|
+
/** The user's stored choice. Irrelevant when an env var overrides it. */
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
/** Random UUIDv4, minted once. Never derived from hardware, paths, or identity. */
|
|
6
|
+
machine_id: string;
|
|
7
|
+
consent_source: ConsentSource;
|
|
8
|
+
/** Gates the one-line notice — see maybeAnnounceFirstRun below. */
|
|
9
|
+
first_run_notice_shown: boolean;
|
|
10
|
+
updated_at: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reads the stored config, minting one (with a fresh machine_id) on first
|
|
14
|
+
* ever call. This is the read path every reporting call goes through — it
|
|
15
|
+
* is cheap (one small JSON file) and idempotent, so there is no reason to
|
|
16
|
+
* cache it across calls within a process; a concurrent `rotifer telemetry
|
|
17
|
+
* off` in another terminal should be picked up by the next report, not
|
|
18
|
+
* shadowed by a stale in-memory copy.
|
|
19
|
+
*/
|
|
20
|
+
export declare function loadOrInitHeartbeatConfig(): HeartbeatConfig;
|
|
21
|
+
/** Backs `rotifer telemetry on|off`. Keeps the existing machine_id. */
|
|
22
|
+
export declare function setHeartbeatEnabled(isEnabled: boolean): HeartbeatConfig;
|
|
23
|
+
export declare function resetHeartbeatIdentity(): void;
|
|
24
|
+
export type HeartbeatDecision = "off-env" | "on-env" | "off-stored" | "on-stored" | "on-default";
|
|
25
|
+
/**
|
|
26
|
+
* Resolution order, mirrored in TELEMETRY.md and kept in sync deliberately:
|
|
27
|
+
* DO_NOT_TRACK / ROTIFER_TELEMETRY (env, either direction) > stored choice
|
|
28
|
+
* > default on.
|
|
29
|
+
*
|
|
30
|
+
* The heartbeat's default is the opposite of gene_invocation_log's
|
|
31
|
+
* (ADR-316 D1 default-off) on purpose — see the migration header on
|
|
32
|
+
* usage_heartbeat for why: this table carries no per-row accountability to
|
|
33
|
+
* protect, and default-off here would just reproduce the blind spot
|
|
34
|
+
* ADR-329 exists to close.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveHeartbeatDecision(config: HeartbeatConfig, env?: NodeJS.ProcessEnv): HeartbeatDecision;
|
|
37
|
+
export declare function heartbeatDecisionEnabled(d: HeartbeatDecision): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Marks the first-run notice as shown and persists it — separated from
|
|
40
|
+
* printing the notice itself (in heartbeat.ts) so the persistence, which
|
|
41
|
+
* must happen exactly once and survive even if the network call that
|
|
42
|
+
* follows fails, is not tangled up with I/O to stderr.
|
|
43
|
+
*/
|
|
44
|
+
export declare function markFirstRunNoticeShown(config: HeartbeatConfig): void;
|
|
45
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/telemetry/config.ts"],"names":[],"mappings":"AA8BA,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,KAAK,GAAG,gBAAgB,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,aAAa,CAAC;IAC9B,mEAAmE;IACnE,sBAAsB,EAAE,OAAO,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAmCD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,IAAI,eAAe,CAa3D;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,OAAO,GAAG,eAAe,CAavE;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAQ7C;AAED,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,QAAQ,GACR,YAAY,GACZ,WAAW,GACX,YAAY,CAAC;AAEjB;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,iBAAiB,CAKnB;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAEtE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAGrE"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadOrInitHeartbeatConfig = loadOrInitHeartbeatConfig;
|
|
4
|
+
exports.setHeartbeatEnabled = setHeartbeatEnabled;
|
|
5
|
+
exports.resetHeartbeatIdentity = resetHeartbeatIdentity;
|
|
6
|
+
exports.resolveHeartbeatDecision = resolveHeartbeatDecision;
|
|
7
|
+
exports.heartbeatDecisionEnabled = heartbeatDecisionEnabled;
|
|
8
|
+
exports.markFirstRunNoticeShown = markFirstRunNoticeShown;
|
|
9
|
+
/**
|
|
10
|
+
* Storage and consent state for the anonymous usage heartbeat (ADR-329).
|
|
11
|
+
*
|
|
12
|
+
* Lives in ~/.rotifer/telemetry.json, the same directory (and the same
|
|
13
|
+
* private-mode convention — see private-fs.ts) as credentials.json and
|
|
14
|
+
* cloud.json. Deliberately the same shape as codegraph's
|
|
15
|
+
* ~/.codegraph/telemetry.json (ADR-329's Context section names it as the
|
|
16
|
+
* design this is measured against): `enabled`, `machine_id`,
|
|
17
|
+
* `consent_source`, `first_run_notice_shown`, `updated_at`.
|
|
18
|
+
*
|
|
19
|
+
* This file is shared, in practice, with rotifer-mcp-server: both processes
|
|
20
|
+
* read and write ~/.rotifer/telemetry.json on the same machine, so a
|
|
21
|
+
* machine_id minted by one is picked up by the other rather than each
|
|
22
|
+
* inventing its own. mcp-server carries its own copy of this module (no
|
|
23
|
+
* cross-package import — they are separate npm packages) but the on-disk
|
|
24
|
+
* shape must stay identical, or one process's write would look like
|
|
25
|
+
* corruption to the other's reader.
|
|
26
|
+
*/
|
|
27
|
+
const node_fs_1 = require("node:fs");
|
|
28
|
+
const node_path_1 = require("node:path");
|
|
29
|
+
const node_crypto_1 = require("node:crypto");
|
|
30
|
+
const private_fs_js_1 = require("../utils/private-fs.js");
|
|
31
|
+
const consent_js_1 = require("./consent.js");
|
|
32
|
+
const ROTIFER_HOME = (0, node_path_1.join)(process.env.HOME || process.env.USERPROFILE || "/tmp", ".rotifer");
|
|
33
|
+
const TELEMETRY_CONFIG_FILE = "telemetry.json";
|
|
34
|
+
function telemetryConfigPath() {
|
|
35
|
+
return (0, node_path_1.join)(ROTIFER_HOME, TELEMETRY_CONFIG_FILE);
|
|
36
|
+
}
|
|
37
|
+
function readStoredConfig() {
|
|
38
|
+
const p = telemetryConfigPath();
|
|
39
|
+
if (!(0, node_fs_1.existsSync)(p))
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse((0, node_fs_1.readFileSync)(p, "utf-8"));
|
|
43
|
+
if (typeof parsed.machine_id !== "string" || !parsed.machine_id)
|
|
44
|
+
return null;
|
|
45
|
+
return {
|
|
46
|
+
enabled: parsed.enabled !== false,
|
|
47
|
+
machine_id: parsed.machine_id,
|
|
48
|
+
consent_source: parsed.consent_source ?? "default-notice",
|
|
49
|
+
first_run_notice_shown: parsed.first_run_notice_shown === true,
|
|
50
|
+
updated_at: parsed.updated_at ?? new Date(0).toISOString(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// A corrupt file is treated the same as a missing one: a fresh
|
|
55
|
+
// machine_id is minted below. Losing continuity on a corrupt file is a
|
|
56
|
+
// acceptable one-time cost; refusing to run telemetry at all until a
|
|
57
|
+
// human fixes the file is not — this is opt-out telemetry, not a wallet.
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function writeStoredConfig(config) {
|
|
62
|
+
(0, private_fs_js_1.ensurePrivateDir)(ROTIFER_HOME);
|
|
63
|
+
const p = telemetryConfigPath();
|
|
64
|
+
(0, node_fs_1.writeFileSync)(p, JSON.stringify(config, null, 2) + "\n", { mode: 0o600 });
|
|
65
|
+
(0, private_fs_js_1.tightenPrivateFile)(p);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads the stored config, minting one (with a fresh machine_id) on first
|
|
69
|
+
* ever call. This is the read path every reporting call goes through — it
|
|
70
|
+
* is cheap (one small JSON file) and idempotent, so there is no reason to
|
|
71
|
+
* cache it across calls within a process; a concurrent `rotifer telemetry
|
|
72
|
+
* off` in another terminal should be picked up by the next report, not
|
|
73
|
+
* shadowed by a stale in-memory copy.
|
|
74
|
+
*/
|
|
75
|
+
function loadOrInitHeartbeatConfig() {
|
|
76
|
+
const existing = readStoredConfig();
|
|
77
|
+
if (existing)
|
|
78
|
+
return existing;
|
|
79
|
+
const fresh = {
|
|
80
|
+
enabled: true,
|
|
81
|
+
machine_id: (0, node_crypto_1.randomUUID)(),
|
|
82
|
+
consent_source: "default-notice",
|
|
83
|
+
first_run_notice_shown: false,
|
|
84
|
+
updated_at: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
writeStoredConfig(fresh);
|
|
87
|
+
return fresh;
|
|
88
|
+
}
|
|
89
|
+
/** Backs `rotifer telemetry on|off`. Keeps the existing machine_id. */
|
|
90
|
+
function setHeartbeatEnabled(isEnabled) {
|
|
91
|
+
const current = loadOrInitHeartbeatConfig();
|
|
92
|
+
const updated = {
|
|
93
|
+
...current,
|
|
94
|
+
enabled: isEnabled,
|
|
95
|
+
consent_source: "cli",
|
|
96
|
+
// Explicitly setting the choice via the CLI counts as having seen
|
|
97
|
+
// whatever notice there was to see — this must not print again.
|
|
98
|
+
first_run_notice_shown: true,
|
|
99
|
+
updated_at: new Date().toISOString(),
|
|
100
|
+
};
|
|
101
|
+
writeStoredConfig(updated);
|
|
102
|
+
return updated;
|
|
103
|
+
}
|
|
104
|
+
function resetHeartbeatIdentity() {
|
|
105
|
+
const p = telemetryConfigPath();
|
|
106
|
+
if ((0, node_fs_1.existsSync)(p)) {
|
|
107
|
+
(0, node_fs_1.writeFileSync)(p, "", { mode: 0o600 });
|
|
108
|
+
}
|
|
109
|
+
// Deleting would also work; overwriting with an empty file and letting the
|
|
110
|
+
// next loadOrInitHeartbeatConfig() call treat it as corrupt-and-remint is
|
|
111
|
+
// simpler than importing unlinkSync for a rarely-used reset path.
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolution order, mirrored in TELEMETRY.md and kept in sync deliberately:
|
|
115
|
+
* DO_NOT_TRACK / ROTIFER_TELEMETRY (env, either direction) > stored choice
|
|
116
|
+
* > default on.
|
|
117
|
+
*
|
|
118
|
+
* The heartbeat's default is the opposite of gene_invocation_log's
|
|
119
|
+
* (ADR-316 D1 default-off) on purpose — see the migration header on
|
|
120
|
+
* usage_heartbeat for why: this table carries no per-row accountability to
|
|
121
|
+
* protect, and default-off here would just reproduce the blind spot
|
|
122
|
+
* ADR-329 exists to close.
|
|
123
|
+
*/
|
|
124
|
+
function resolveHeartbeatDecision(config, env = process.env) {
|
|
125
|
+
if ((0, consent_js_1.telemetryOptedOutByEnv)(env))
|
|
126
|
+
return "off-env";
|
|
127
|
+
if ((0, consent_js_1.telemetryExplicitlyOnByEnv)(env))
|
|
128
|
+
return "on-env";
|
|
129
|
+
if (!config.enabled)
|
|
130
|
+
return "off-stored";
|
|
131
|
+
return config.consent_source === "default-notice" ? "on-default" : "on-stored";
|
|
132
|
+
}
|
|
133
|
+
function heartbeatDecisionEnabled(d) {
|
|
134
|
+
return d === "on-env" || d === "on-stored" || d === "on-default";
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Marks the first-run notice as shown and persists it — separated from
|
|
138
|
+
* printing the notice itself (in heartbeat.ts) so the persistence, which
|
|
139
|
+
* must happen exactly once and survive even if the network call that
|
|
140
|
+
* follows fails, is not tangled up with I/O to stderr.
|
|
141
|
+
*/
|
|
142
|
+
function markFirstRunNoticeShown(config) {
|
|
143
|
+
if (config.first_run_notice_shown)
|
|
144
|
+
return;
|
|
145
|
+
writeStoredConfig({ ...config, first_run_notice_shown: true, updated_at: new Date().toISOString() });
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/telemetry/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one environment-variable check both telemetry paths share.
|
|
3
|
+
*
|
|
4
|
+
* ADR-329's decision is explicit: "ROTIFER_TELEMETRY=0 同时关闭匿名信号与
|
|
5
|
+
* 登录上报——「关掉」意味着全部,与 316 的语义一致." Before this module
|
|
6
|
+
* existed, `ROTIFER_TELEMETRY` was checked in exactly one place
|
|
7
|
+
* (cloud/invocation.ts, for the signed-in Gene invocation report). Adding
|
|
8
|
+
* the anonymous heartbeat with its own copy of that check would have been
|
|
9
|
+
* the easy path and the wrong one: two copies of "is telemetry off" drift
|
|
10
|
+
* the moment either one changes, and the failure mode is silent — a user
|
|
11
|
+
* who set ROTIFER_TELEMETRY=0 expecting it to mean everything would keep
|
|
12
|
+
* being counted by whichever path didn't get updated.
|
|
13
|
+
*
|
|
14
|
+
* DO_NOT_TRACK is the cross-tool convention (https://consoledonottrack.com/)
|
|
15
|
+
* and is new here — ADR-316 predates it. It outranks ROTIFER_TELEMETRY
|
|
16
|
+
* because it is a stance the user took before ever hearing of this project.
|
|
17
|
+
*/
|
|
18
|
+
export declare function telemetryOptedOutByEnv(env?: NodeJS.ProcessEnv): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* True when the environment explicitly asked for telemetry (as opposed to
|
|
21
|
+
* "unset, so whatever the default is"). Distinguishing "explicitly on" from
|
|
22
|
+
* "unset" matters for the heartbeat: an explicit ROTIFER_TELEMETRY=1 should
|
|
23
|
+
* win even over a stored "disabled" choice, the same way `codegraph
|
|
24
|
+
* TELEMETRY=1` does — a per-shell override should not require first running
|
|
25
|
+
* a separate command to change stored state.
|
|
26
|
+
*/
|
|
27
|
+
export declare function telemetryExplicitlyOnByEnv(env?: NodeJS.ProcessEnv): boolean;
|
|
28
|
+
//# sourceMappingURL=consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/telemetry/consent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAMpF;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAIxF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.telemetryOptedOutByEnv = telemetryOptedOutByEnv;
|
|
4
|
+
exports.telemetryExplicitlyOnByEnv = telemetryExplicitlyOnByEnv;
|
|
5
|
+
/**
|
|
6
|
+
* The one environment-variable check both telemetry paths share.
|
|
7
|
+
*
|
|
8
|
+
* ADR-329's decision is explicit: "ROTIFER_TELEMETRY=0 同时关闭匿名信号与
|
|
9
|
+
* 登录上报——「关掉」意味着全部,与 316 的语义一致." Before this module
|
|
10
|
+
* existed, `ROTIFER_TELEMETRY` was checked in exactly one place
|
|
11
|
+
* (cloud/invocation.ts, for the signed-in Gene invocation report). Adding
|
|
12
|
+
* the anonymous heartbeat with its own copy of that check would have been
|
|
13
|
+
* the easy path and the wrong one: two copies of "is telemetry off" drift
|
|
14
|
+
* the moment either one changes, and the failure mode is silent — a user
|
|
15
|
+
* who set ROTIFER_TELEMETRY=0 expecting it to mean everything would keep
|
|
16
|
+
* being counted by whichever path didn't get updated.
|
|
17
|
+
*
|
|
18
|
+
* DO_NOT_TRACK is the cross-tool convention (https://consoledonottrack.com/)
|
|
19
|
+
* and is new here — ADR-316 predates it. It outranks ROTIFER_TELEMETRY
|
|
20
|
+
* because it is a stance the user took before ever hearing of this project.
|
|
21
|
+
*/
|
|
22
|
+
function telemetryOptedOutByEnv(env = process.env) {
|
|
23
|
+
const dnt = (env.DO_NOT_TRACK || "").trim().toLowerCase();
|
|
24
|
+
if (dnt !== "" && dnt !== "0" && dnt !== "false")
|
|
25
|
+
return true;
|
|
26
|
+
const flag = (env.ROTIFER_TELEMETRY || "").trim().toLowerCase();
|
|
27
|
+
return flag === "0" || flag === "false" || flag === "off";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* True when the environment explicitly asked for telemetry (as opposed to
|
|
31
|
+
* "unset, so whatever the default is"). Distinguishing "explicitly on" from
|
|
32
|
+
* "unset" matters for the heartbeat: an explicit ROTIFER_TELEMETRY=1 should
|
|
33
|
+
* win even over a stored "disabled" choice, the same way `codegraph
|
|
34
|
+
* TELEMETRY=1` does — a per-shell override should not require first running
|
|
35
|
+
* a separate command to change stored state.
|
|
36
|
+
*/
|
|
37
|
+
function telemetryExplicitlyOnByEnv(env = process.env) {
|
|
38
|
+
if (telemetryOptedOutByEnv(env))
|
|
39
|
+
return false;
|
|
40
|
+
const flag = (env.ROTIFER_TELEMETRY || "").trim().toLowerCase();
|
|
41
|
+
return flag !== "" && flag !== "0" && flag !== "false" && flag !== "off";
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/telemetry/consent.ts"],"names":[],"mappings":";;AAiBA,wDAMC;AAUD,gEAIC;AArCD;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,sBAAsB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACzE,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1D,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IAE9D,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,OAAO,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,0BAA0B,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7E,IAAI,sBAAsB,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,OAAO,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Longest a caller will wait for a heartbeat to settle before exiting anyway. */
|
|
2
|
+
export declare const FLUSH_TIMEOUT_MS = 2000;
|
|
3
|
+
/**
|
|
4
|
+
* Wait for any in-flight heartbeat report to settle. Call this before
|
|
5
|
+
* `process.exit()` — and before returning from a command that is about to let
|
|
6
|
+
* the process exit on its own, since that path turned out not to be safe
|
|
7
|
+
* either (see this file's top comment). A run must never hang on telemetry,
|
|
8
|
+
* so it gives up after timeoutMs and resolves regardless.
|
|
9
|
+
*/
|
|
10
|
+
export declare function flushHeartbeat(timeoutMs?: number): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Reports one Gene invocation's worth of activity for this machine, today,
|
|
13
|
+
* on the given channel. Fire-and-forget; every failure mode is silence,
|
|
14
|
+
* same contract as recordGeneInvocation. Safe to call unconditionally from
|
|
15
|
+
* every call site that already calls recordGeneInvocation — this function
|
|
16
|
+
* does its own consent and test-run checks. The request it sends is tracked
|
|
17
|
+
* in `inFlight` until it settles — see flushHeartbeat.
|
|
18
|
+
*/
|
|
19
|
+
export declare function recordHeartbeat(): void;
|
|
20
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../src/telemetry/heartbeat.ts"],"names":[],"mappings":"AA0EA,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,SAAS,GAAE,MAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CASxF;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAkFtC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FLUSH_TIMEOUT_MS = void 0;
|
|
4
|
+
exports.flushHeartbeat = flushHeartbeat;
|
|
5
|
+
exports.recordHeartbeat = recordHeartbeat;
|
|
6
|
+
/**
|
|
7
|
+
* Anonymous usage heartbeat (ADR-329).
|
|
8
|
+
*
|
|
9
|
+
* Answers a narrower question than gene_invocation_log: not "who called
|
|
10
|
+
* what", just "did this machine run something today, and through which
|
|
11
|
+
* channel". No identity, on by default, and — unlike the invocation report
|
|
12
|
+
* — it costs nothing to lose one. It is not §33.4 input; a dropped
|
|
13
|
+
* heartbeat is not a hole in an audited ledger, it is one machine's "active
|
|
14
|
+
* today" signal that will show up again tomorrow if the machine is still in
|
|
15
|
+
* use.
|
|
16
|
+
*
|
|
17
|
+
* This file used to not track in-flight requests at all, on the theory that
|
|
18
|
+
* "Node drains the pending request before the event loop empties" (the same
|
|
19
|
+
* assumption cloud/invocation.ts's success path relies on) made it
|
|
20
|
+
* unnecessary here. A real end-to-end run against production — `rotifer run`
|
|
21
|
+
* on a freshly-provisioned machine, 2026-08-30 — showed that assumption does
|
|
22
|
+
* not hold for this fetch: the CLI printed the first-run notice (proof the
|
|
23
|
+
* request was sent) and exited 0, and the row never arrived. A manual POST
|
|
24
|
+
* with the same body against the same machine_id landed immediately, which
|
|
25
|
+
* rules out the RPC, the grant, and the endpoint — the only thing different
|
|
26
|
+
* was that the CLI process existed for a few hundred more milliseconds. So
|
|
27
|
+
* this now tracks in-flight requests the same way cloud/invocation.ts does,
|
|
28
|
+
* and every call site that can exit soon after calling recordHeartbeat()
|
|
29
|
+
* must await flushHeartbeat() first, exactly like flushInvocationReports().
|
|
30
|
+
*/
|
|
31
|
+
const node_fs_1 = require("node:fs");
|
|
32
|
+
const node_path_1 = require("node:path");
|
|
33
|
+
const client_js_1 = require("../cloud/client.js");
|
|
34
|
+
const invocation_js_1 = require("../cloud/invocation.js");
|
|
35
|
+
const config_js_1 = require("./config.js");
|
|
36
|
+
// __dirname is CommonJS's own global here (see tsconfig's "module": "NodeNext"
|
|
37
|
+
// targeting CJS output) — no fileURLToPath(import.meta.url) needed, and that
|
|
38
|
+
// ESM-only syntax would fail to compile in this build target anyway.
|
|
39
|
+
let cachedVersion = null;
|
|
40
|
+
function packageVersion() {
|
|
41
|
+
if (cachedVersion !== null)
|
|
42
|
+
return cachedVersion;
|
|
43
|
+
try {
|
|
44
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "..", "..", "package.json"), "utf-8"));
|
|
45
|
+
cachedVersion = typeof pkg.version === "string" ? pkg.version : null;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
cachedVersion = null;
|
|
49
|
+
}
|
|
50
|
+
return cachedVersion;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The one-line disclosure, printed to stderr exactly once, before the first
|
|
54
|
+
* heartbeat this machine ever actually sends — never before, so a machine
|
|
55
|
+
* whose heartbeat is off (env or stored choice) never sees a notice about
|
|
56
|
+
* data that is never leaving it. Mirrors codegraph's wording pattern
|
|
57
|
+
* (ADR-329 measured its design against codegraph's, verified against its
|
|
58
|
+
* compiled output) but says MCP explicitly, because stderr is where CLI
|
|
59
|
+
* users look and exactly where MCP hosts do not — the MCP side of this
|
|
60
|
+
* (rotifer-mcp-server) carries the notice in a tool response instead, for
|
|
61
|
+
* that reason.
|
|
62
|
+
*/
|
|
63
|
+
function printFirstRunNotice() {
|
|
64
|
+
process.stderr.write("[rotifer] Anonymous usage heartbeat is on by default — no code, no identity, just " +
|
|
65
|
+
"\"this machine ran something today\". `rotifer telemetry off`, ROTIFER_TELEMETRY=0, " +
|
|
66
|
+
"or DO_NOT_TRACK=1 disables it. Details: https://rotifer.dev/telemetry\n");
|
|
67
|
+
}
|
|
68
|
+
/** Heartbeat reports still in flight. Emptied as each settles; see flushHeartbeat. */
|
|
69
|
+
const inFlight = new Set();
|
|
70
|
+
/** Longest a caller will wait for a heartbeat to settle before exiting anyway. */
|
|
71
|
+
exports.FLUSH_TIMEOUT_MS = 2000;
|
|
72
|
+
/**
|
|
73
|
+
* Wait for any in-flight heartbeat report to settle. Call this before
|
|
74
|
+
* `process.exit()` — and before returning from a command that is about to let
|
|
75
|
+
* the process exit on its own, since that path turned out not to be safe
|
|
76
|
+
* either (see this file's top comment). A run must never hang on telemetry,
|
|
77
|
+
* so it gives up after timeoutMs and resolves regardless.
|
|
78
|
+
*/
|
|
79
|
+
async function flushHeartbeat(timeoutMs = exports.FLUSH_TIMEOUT_MS) {
|
|
80
|
+
if (inFlight.size === 0)
|
|
81
|
+
return;
|
|
82
|
+
let timer;
|
|
83
|
+
const deadline = new Promise((resolve) => {
|
|
84
|
+
timer = setTimeout(resolve, timeoutMs);
|
|
85
|
+
timer.unref?.();
|
|
86
|
+
});
|
|
87
|
+
await Promise.race([Promise.allSettled([...inFlight]).then(() => undefined), deadline]);
|
|
88
|
+
if (timer)
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Reports one Gene invocation's worth of activity for this machine, today,
|
|
93
|
+
* on the given channel. Fire-and-forget; every failure mode is silence,
|
|
94
|
+
* same contract as recordGeneInvocation. Safe to call unconditionally from
|
|
95
|
+
* every call site that already calls recordGeneInvocation — this function
|
|
96
|
+
* does its own consent and test-run checks. The request it sends is tracked
|
|
97
|
+
* in `inFlight` until it settles — see flushHeartbeat.
|
|
98
|
+
*/
|
|
99
|
+
function recordHeartbeat() {
|
|
100
|
+
// The whole body is one try/catch, deliberately wider than the network
|
|
101
|
+
// call. loadOrInitHeartbeatConfig()/markFirstRunNoticeShown() write to
|
|
102
|
+
// ~/.rotifer/telemetry.json — config.ts does not swallow that failure
|
|
103
|
+
// (rotifer telemetry on/off should surface a write error to the user who
|
|
104
|
+
// ran it), but a Gene execution silently failing because this
|
|
105
|
+
// fire-and-forget signal couldn't touch the filesystem would be a much
|
|
106
|
+
// worse trade. Every failure mode here is silence, same contract as the
|
|
107
|
+
// network call below.
|
|
108
|
+
try {
|
|
109
|
+
if ((0, invocation_js_1.runningUnderTest)())
|
|
110
|
+
return;
|
|
111
|
+
const channel = (0, invocation_js_1.resolveInvocationChannel)();
|
|
112
|
+
if (channel === null)
|
|
113
|
+
return; // unattributable declared channel — see resolveInvocationChannel
|
|
114
|
+
const config = (0, config_js_1.loadOrInitHeartbeatConfig)();
|
|
115
|
+
const decision = (0, config_js_1.resolveHeartbeatDecision)(config);
|
|
116
|
+
if (!(0, config_js_1.heartbeatDecisionEnabled)(decision))
|
|
117
|
+
return;
|
|
118
|
+
// Only reached once we know a report is actually about to leave the
|
|
119
|
+
// process — see printFirstRunNotice's doc comment for why the ordering
|
|
120
|
+
// matters. Persisted synchronously so a crash in the fetch below can
|
|
121
|
+
// never cause the notice to reprint on the next run.
|
|
122
|
+
if (!config.first_run_notice_shown) {
|
|
123
|
+
printFirstRunNotice();
|
|
124
|
+
(0, config_js_1.markFirstRunNoticeShown)(config);
|
|
125
|
+
}
|
|
126
|
+
const cloudConfig = (0, client_js_1.loadCloudConfig)();
|
|
127
|
+
const url = `${cloudConfig.endpoint.replace(/\/+$/, "")}/rest/v1/rpc/record_heartbeat`;
|
|
128
|
+
// flushHeartbeat() giving up after FLUSH_TIMEOUT_MS only stops the
|
|
129
|
+
// *caller* from waiting — it does not by itself end this fetch. A
|
|
130
|
+
// request that never gets a response (the fake-timeout regression test
|
|
131
|
+
// in tests/e2e/telemetry-heartbeat-delivery.test.ts exercises exactly
|
|
132
|
+
// this) leaves its socket open as an active handle, and Node does not
|
|
133
|
+
// exit while one exists — so without this abort, a stalled telemetry
|
|
134
|
+
// endpoint would hang every `rotifer run` for however long the OS's own
|
|
135
|
+
// TCP timeout takes, which is far worse than the bug this file was
|
|
136
|
+
// rewritten to fix. Aborting on the same deadline is what actually lets
|
|
137
|
+
// the process exit on schedule instead of merely stopping user code
|
|
138
|
+
// from waiting on it.
|
|
139
|
+
const controller = new AbortController();
|
|
140
|
+
const abortTimer = setTimeout(() => controller.abort(), exports.FLUSH_TIMEOUT_MS);
|
|
141
|
+
abortTimer.unref?.();
|
|
142
|
+
// No Authorization header: record_heartbeat is anon-callable by design
|
|
143
|
+
// (migration 20260830010000) — that grant is the entire point of ADR-329.
|
|
144
|
+
// Sending credentials here would be pointless (the RPC does not use them)
|
|
145
|
+
// and would defeat the "no identity" promise for a user who happens to be
|
|
146
|
+
// signed in but has not opted into the signed-in invocation report.
|
|
147
|
+
const settled = fetch(url, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: { "Content-Type": "application/json", apikey: cloudConfig.anonKey },
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
p_machine_id: config.machine_id,
|
|
152
|
+
p_channel: channel,
|
|
153
|
+
p_client_version: packageVersion(),
|
|
154
|
+
p_invocation_delta: 1,
|
|
155
|
+
}),
|
|
156
|
+
signal: controller.signal,
|
|
157
|
+
})
|
|
158
|
+
.then((res) => {
|
|
159
|
+
if (!res.ok && process.env.ROTIFER_DEBUG) {
|
|
160
|
+
process.stderr.write(`[rotifer] record_heartbeat failed (${res.status})\n`);
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
.catch((err) => {
|
|
164
|
+
if (process.env.ROTIFER_DEBUG) {
|
|
165
|
+
process.stderr.write(`[rotifer] record_heartbeat error: ${err?.message ?? err}\n`);
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
.finally(() => {
|
|
169
|
+
clearTimeout(abortTimer);
|
|
170
|
+
inFlight.delete(settled);
|
|
171
|
+
});
|
|
172
|
+
inFlight.add(settled);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (process.env.ROTIFER_DEBUG) {
|
|
176
|
+
process.stderr.write(`[rotifer] record_heartbeat setup error: ${err?.message ?? err}\n`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=heartbeat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.js","sourceRoot":"","sources":["../../src/telemetry/heartbeat.ts"],"names":[],"mappings":"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"}
|