prreviewbuddy 0.29.1 → 0.30.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.
- package/CHANGELOG.md +22 -0
- package/README.md +9 -8
- package/dist/{delete_review-DUCSC5Bc.js → delete_review-B_2J2Wn9.js} +17 -11
- package/dist/main.js +102 -108
- package/dist/server.js +15 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.30.0
|
|
4
|
+
|
|
5
|
+
**Usage telemetry is now on by default.** Non-identifying usage telemetry is now enabled by default
|
|
6
|
+
on installations that have not previously made a choice. Existing opt-outs remain respected: if you
|
|
7
|
+
answered no when PR Review Buddy asked, nothing is sent, and nothing about your machine changes.
|
|
8
|
+
The first-run question is gone, replaced by a one-time notice in the terminal that says what is
|
|
9
|
+
sent and how to stop it. That notice is shown only to installations the new default actually
|
|
10
|
+
changes, so a machine that already answered sees nothing.
|
|
11
|
+
|
|
12
|
+
Nothing new is collected. The same two events carry the same eight fields they did before: review
|
|
13
|
+
start and finish, a random installation ID, the version, the platform, the agent, whether the
|
|
14
|
+
analysis succeeded and how long it took. Never your code, diffs, file paths, repository names,
|
|
15
|
+
prompts or review content. Rows are still deleted after 90 days.
|
|
16
|
+
|
|
17
|
+
`prreviewbuddy config set telemetry-upload off` turns it off, as does the Privacy section of the
|
|
18
|
+
workspace Settings page, and `PRB_TELEMETRY=off` still stops the local log as well. The reasoning
|
|
19
|
+
for the change, and what it replaced, is written out at https://prreviewbuddy.com/privacy.
|
|
20
|
+
|
|
21
|
+
**Copy says "non-identifying" rather than "anonymous".** The events carry a random installation ID
|
|
22
|
+
that persists, and a stable identifier makes data pseudonymous however little else is attached to
|
|
23
|
+
it. The word changed; what is sent did not.
|
|
24
|
+
|
|
3
25
|
## 0.29.1
|
|
4
26
|
|
|
5
27
|
**Icons in the review sidebar.** Every item in a review's sidebar now has an icon: Review, KISS,
|
package/README.md
CHANGED
|
@@ -44,10 +44,11 @@ an assistant that has read the diff. The job is durable, so closing the terminal
|
|
|
44
44
|
Your code goes to the coding agent you chose, exactly as it would if you had run that agent
|
|
45
45
|
yourself. It does not pass through us, and no server of ours is in the path of a review.
|
|
46
46
|
|
|
47
|
-
PR Review Buddy
|
|
48
|
-
names, a random ID generated on your machine, the version, the platform, the agent and a
|
|
49
|
-
Never your code, diffs, file paths, repository names, prompts or review content
|
|
50
|
-
|
|
47
|
+
PR Review Buddy sends non-identifying usage telemetry, on by default and off with one command: two
|
|
48
|
+
event names, a random ID generated on your machine, the version, the platform, the agent and a
|
|
49
|
+
duration. Never your code, diffs, file paths, repository names, prompts or review content.
|
|
50
|
+
`prreviewbuddy config set telemetry-upload off` stops it.
|
|
51
|
+
[The full field table is published](https://prreviewbuddy.com/privacy).
|
|
51
52
|
|
|
52
53
|
More at [prreviewbuddy.com](https://prreviewbuddy.com).
|
|
53
54
|
|
|
@@ -80,7 +81,7 @@ prreviewbuddy open <review> open one review
|
|
|
80
81
|
prreviewbuddy agents which coding agents you have
|
|
81
82
|
prreviewbuddy config set agent <agent> remember which to use when you do not name one
|
|
82
83
|
prreviewbuddy config set telemetry-upload on|off
|
|
83
|
-
send
|
|
84
|
+
send usage telemetry, or stop. On by default
|
|
84
85
|
|
|
85
86
|
prreviewbuddy uninstall remove everything this put on your machine
|
|
86
87
|
```
|
|
@@ -167,10 +168,10 @@ ID** and **Delete review**, and the menu on a card deletes every review of that
|
|
|
167
168
|
</details>
|
|
168
169
|
|
|
169
170
|
<details>
|
|
170
|
-
<summary><b>
|
|
171
|
+
<summary><b>Usage telemetry</b> — what is sent, and how to stop it</summary>
|
|
171
172
|
|
|
172
|
-
Two events reach a server we run,
|
|
173
|
-
`
|
|
173
|
+
Two events reach a server we run, on by default since 0.30.0: `review_started` and
|
|
174
|
+
`review_finished`. Between them they carry a random UUID generated on your
|
|
174
175
|
machine, the CLI version, the platform, which agent ran the analysis, a timestamp, whether it
|
|
175
176
|
succeeded and how long it took. That is the whole list, and the server refuses any field that is
|
|
176
177
|
not on it rather than accepting the request and dropping what it does not recognise.
|
|
@@ -9298,26 +9298,32 @@ async function withUsageRecorded(workspaceId, kind, env, body, runId, requestedM
|
|
|
9298
9298
|
*/
|
|
9299
9299
|
var UPLOAD_KEY = "telemetryUpload";
|
|
9300
9300
|
/**
|
|
9301
|
-
*
|
|
9301
|
+
* What this machine was told to do about transmission, or `null` if nobody ever said.
|
|
9302
9302
|
*
|
|
9303
|
-
* Three states rather than two, and the third is load-bearing
|
|
9304
|
-
*
|
|
9305
|
-
*
|
|
9303
|
+
* Three states rather than two, and the third is load-bearing in both directions: it is what lets
|
|
9304
|
+
* `upload.ts` apply a default without overwriting anybody's answer, and what lets the CLI disclose
|
|
9305
|
+
* the default to the machines it actually applies to and nobody else.
|
|
9306
9306
|
*/
|
|
9307
9307
|
function telemetryUploadConsent() {
|
|
9308
9308
|
return loadConfig().telemetryUpload;
|
|
9309
9309
|
}
|
|
9310
|
-
/**
|
|
9310
|
+
/**
|
|
9311
|
+
* Whether a subset may go over the wire. Yes unless somebody said no.
|
|
9312
|
+
*
|
|
9313
|
+
* `!== false` rather than `=== true`, and that is the whole of the 0.30.0 change in one operator.
|
|
9314
|
+
* Opt in measured who answers questions rather than who uses this: one or two installations
|
|
9315
|
+
* reporting against hundreds of weekly downloads. What is sent did not widen by a field.
|
|
9316
|
+
*/
|
|
9311
9317
|
function telemetryUploadEnabled() {
|
|
9312
9318
|
const config = loadConfig();
|
|
9313
|
-
return config.telemetry && config.telemetryUpload
|
|
9319
|
+
return config.telemetry && config.telemetryUpload !== false;
|
|
9314
9320
|
}
|
|
9315
9321
|
/**
|
|
9316
|
-
* Remember
|
|
9322
|
+
* Remember a decision somebody made, so it outlives any change to the default.
|
|
9317
9323
|
*
|
|
9318
9324
|
* Read-modify-write through `config_file.ts`, and refuses outright while `PRB_TELEMETRY=off` is
|
|
9319
|
-
* set: the kill switch means nothing is
|
|
9320
|
-
* would record a decision nobody made.
|
|
9325
|
+
* set: the kill switch means nothing is being sent and nobody is being asked anything, so there is
|
|
9326
|
+
* no answer to store, and writing one would record a decision nobody made.
|
|
9321
9327
|
*/
|
|
9322
9328
|
function recordTelemetryUploadConsent(answer) {
|
|
9323
9329
|
if (process.env.PRB_TELEMETRY === "off") return;
|
|
@@ -9366,7 +9372,7 @@ function loadConfig() {
|
|
|
9366
9372
|
} catch {}
|
|
9367
9373
|
return config;
|
|
9368
9374
|
}
|
|
9369
|
-
/** Anything that is not exactly `true` or `false` is nobody having
|
|
9375
|
+
/** Anything that is not exactly `true` or `false` is nobody having chosen. */
|
|
9370
9376
|
function consentOf(value) {
|
|
9371
9377
|
return typeof value === "boolean" ? value : null;
|
|
9372
9378
|
}
|
|
@@ -9415,7 +9421,7 @@ function project(record) {
|
|
|
9415
9421
|
} : base;
|
|
9416
9422
|
}
|
|
9417
9423
|
/**
|
|
9418
|
-
* Send one event, if there is one to send and
|
|
9424
|
+
* Send one event, if there is one to send and nobody has said not to.
|
|
9419
9425
|
*
|
|
9420
9426
|
* Returns nothing and awaits nothing. A failed send is a lost data point, which is a cost we
|
|
9421
9427
|
* accept; a thrown error here would be a review that did not happen, which is not. The same
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $t as writeAgentPreference, A as removeWorktree, B as workspaceUrl, C as recordTelemetryUploadConsent, Dt as saveWorkspace, Et as reviewedRepositories, G as DEFAULT_WORKSPACE_PORT, H as BUILD_VERSION, I as ensureServer, J as parseWorkspacePort, Jt as git, K as MAX_WORKSPACE_PORT, Kt as resolveTarget, M as relativeTime, N as readIndexToken, O as MANAGED_ROOT, Ot as summarise, P as bootstrapUrl, Q as forgeResolver, Qt as readAgentPreference, R as reviewsUrl, S as record, St as positionInLineage, U as PACKAGE_NAME, X as statedWorkspacePort, Xt as resolveModel, Yt as resolveAgent, Z as writePortPreference, a as wasBlocked, at as isTerminal, b as startJob, bt as loadWorkspace, c as updateReview, ct as PHASES, dt as purposeOf, en as CONFIG_PATH, f as UpdateAlreadyRunningError, ft as clearClaim, g as recordKissRun, ht as groupByLineage, i as liveJobsFor, in as agentById, j as modelHelpLines, k as readMarker, m as runningJobs, mt as followedRefName, n as deleteReview$1, nn as AGENT_IDS, nt as allJobs, on as detectAgents, ot as loadJob, p as liveUpdateFor, pt as isClaimed, q as MIN_WORKSPACE_PORT, qt as displayRef, rn as DEFAULT_AGENT_ID, tn as STORE_ROOT, tt as checkFreshness, ut as progressSteps, v as reanalyseReview, vt as lineageKeyFor, w as telemetryUploadConsent, wt as recentWorkspaces, xt as matchingWorkspaceIds, y as runJob, z as stopServer } from "./delete_review-
|
|
2
|
+
import { $t as writeAgentPreference, A as removeWorktree, B as workspaceUrl, C as recordTelemetryUploadConsent, Dt as saveWorkspace, Et as reviewedRepositories, G as DEFAULT_WORKSPACE_PORT, H as BUILD_VERSION, I as ensureServer, J as parseWorkspacePort, Jt as git, K as MAX_WORKSPACE_PORT, Kt as resolveTarget, M as relativeTime, N as readIndexToken, O as MANAGED_ROOT, Ot as summarise, P as bootstrapUrl, Q as forgeResolver, Qt as readAgentPreference, R as reviewsUrl, S as record$1, St as positionInLineage, U as PACKAGE_NAME, X as statedWorkspacePort, Xt as resolveModel, Yt as resolveAgent, Z as writePortPreference, a as wasBlocked, at as isTerminal, b as startJob, bt as loadWorkspace, c as updateReview, ct as PHASES, dt as purposeOf, en as CONFIG_PATH, f as UpdateAlreadyRunningError, ft as clearClaim, g as recordKissRun, ht as groupByLineage, i as liveJobsFor, in as agentById, j as modelHelpLines, k as readMarker, m as runningJobs, mt as followedRefName, n as deleteReview$1, nn as AGENT_IDS, nt as allJobs, on as detectAgents, ot as loadJob, p as liveUpdateFor, pt as isClaimed, q as MIN_WORKSPACE_PORT, qt as displayRef, rn as DEFAULT_AGENT_ID, tn as STORE_ROOT, tt as checkFreshness, ut as progressSteps, v as reanalyseReview, vt as lineageKeyFor, w as telemetryUploadConsent, wt as recentWorkspaces, xt as matchingWorkspaceIds, y as runJob, z as stopServer } from "./delete_review-B_2J2Wn9.js";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { existsSync, mkdirSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -987,9 +987,9 @@ your own checkout is never read or written while it works.
|
|
|
987
987
|
prreviewbuddy config get agent the coding agent used when you do not name one
|
|
988
988
|
prreviewbuddy config set agent <agent> remember ${AGENT_NAMES}
|
|
989
989
|
prreviewbuddy config get telemetry-upload
|
|
990
|
-
whether
|
|
990
|
+
whether usage telemetry is sent. On by default
|
|
991
991
|
prreviewbuddy config set telemetry-upload on|off
|
|
992
|
-
send
|
|
992
|
+
send it, or stop
|
|
993
993
|
prreviewbuddy config get port the port the workspace runs on
|
|
994
994
|
prreviewbuddy config set port <port> move it, from the next start onwards
|
|
995
995
|
prreviewbuddy config path where these settings are kept, to edit by hand
|
|
@@ -1119,48 +1119,46 @@ function unknownSetting(key, known) {
|
|
|
1119
1119
|
return `There is no setting called \`${key}\`. This can read and set: ${known.join(", ")}.`;
|
|
1120
1120
|
}
|
|
1121
1121
|
/**
|
|
1122
|
-
* The
|
|
1122
|
+
* The one-time disclosure, in four lines.
|
|
1123
1123
|
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
*
|
|
1127
|
-
*
|
|
1124
|
+
* Four because a first run is not the place for a privacy policy, and the policy is one line away.
|
|
1125
|
+
* It names both lists, and the one that is never sent is the longer of the two: printing the longer
|
|
1126
|
+
* one is the honest way round when the thing being disclosed is on without anybody asking.
|
|
1127
|
+
*
|
|
1128
|
+
* "Non-identifying" rather than "anonymous", which this copy said until 0.30.0. The payload carries
|
|
1129
|
+
* a random installation UUID that persists, and a stable identifier makes data pseudonymous however
|
|
1130
|
+
* little else is attached to it. "Anonymous" is a word with a technical meaning strong enough to be
|
|
1131
|
+
* challenged on, and the claim below does not need it to be true.
|
|
1128
1132
|
*/
|
|
1129
|
-
function
|
|
1133
|
+
function telemetryNotice() {
|
|
1130
1134
|
return [
|
|
1131
1135
|
"",
|
|
1132
|
-
"
|
|
1133
|
-
"Never your code, diffs,
|
|
1134
|
-
"
|
|
1136
|
+
"Non-identifying usage telemetry is on by default: review start and finish, version,",
|
|
1137
|
+
"platform, agent, success and duration. Never your code, diffs, repository names or",
|
|
1138
|
+
"review content.",
|
|
1139
|
+
"Turn it off: `prreviewbuddy config set telemetry-upload off`",
|
|
1140
|
+
"Details: https://prreviewbuddy.com/privacy",
|
|
1135
1141
|
""
|
|
1136
1142
|
].join("\n");
|
|
1137
1143
|
}
|
|
1138
|
-
function telemetryUploadUnclear() {
|
|
1139
|
-
return "Please answer y or n.\n";
|
|
1140
|
-
}
|
|
1141
|
-
function telemetryUploadGranted() {
|
|
1142
|
-
return "Thank you. Turn it off whenever you like with `prreviewbuddy config set telemetry-upload off`.";
|
|
1143
|
-
}
|
|
1144
|
-
function telemetryUploadDeclined() {
|
|
1145
|
-
return "Nothing will be sent. Turn it on with `prreviewbuddy config set telemetry-upload on`.";
|
|
1146
|
-
}
|
|
1147
1144
|
/**
|
|
1148
|
-
* What `config get telemetry-upload` says, including for the machine that
|
|
1145
|
+
* What `config get telemetry-upload` says, including for the machine that never chose.
|
|
1149
1146
|
*
|
|
1150
|
-
* Three answers rather than two,
|
|
1151
|
-
*
|
|
1147
|
+
* Three answers rather than two, and the third survives the move to a default because it still
|
|
1148
|
+
* names a different fact: "on because that is the default" and "on because you turned it on" are
|
|
1149
|
+
* the same behaviour arrived at two ways, and only one of them is a decision somebody made. The
|
|
1150
|
+
* same distinction `noAgentPreference` keeps next door.
|
|
1152
1151
|
*/
|
|
1153
1152
|
function telemetryUploadIs(consent) {
|
|
1154
1153
|
if (consent === null) return [
|
|
1155
|
-
"
|
|
1154
|
+
"Non-identifying usage telemetry is on by default, and you have not changed it.",
|
|
1156
1155
|
"",
|
|
1157
|
-
"
|
|
1158
|
-
"Answer it now with `prreviewbuddy config set telemetry-upload on` or `off`."
|
|
1156
|
+
"Turn it off with `prreviewbuddy config set telemetry-upload off`."
|
|
1159
1157
|
].join("\n");
|
|
1160
|
-
return consent ? "
|
|
1158
|
+
return consent ? "Non-identifying usage telemetry is being sent, because you turned it on. Turn it off with `prreviewbuddy config set telemetry-upload off`." : "Usage telemetry is not being sent, because you turned it off.";
|
|
1161
1159
|
}
|
|
1162
1160
|
function telemetryUploadSet(on) {
|
|
1163
|
-
return on ? "
|
|
1161
|
+
return on ? "Non-identifying usage telemetry will be sent. Never your code, diffs, file paths, repository names, prompts or review content." : "Nothing will be sent. The usage log on this machine is unchanged; `PRB_TELEMETRY=off` turns that off too.";
|
|
1164
1162
|
}
|
|
1165
1163
|
/**
|
|
1166
1164
|
* The workspace port as it stands, and how it is changed.
|
|
@@ -2384,7 +2382,7 @@ async function operate(command, workspace) {
|
|
|
2384
2382
|
}
|
|
2385
2383
|
workspace.lastUpdate = outcome;
|
|
2386
2384
|
saveWorkspace(workspace);
|
|
2387
|
-
record({
|
|
2385
|
+
record$1({
|
|
2388
2386
|
event: "review_updated",
|
|
2389
2387
|
reviewId: workspace.id,
|
|
2390
2388
|
ok: outcome.ok,
|
|
@@ -3214,7 +3212,11 @@ async function uninstall(command, deps = REAL) {
|
|
|
3214
3212
|
//#endregion
|
|
3215
3213
|
//#region src/first_run.ts
|
|
3216
3214
|
/**
|
|
3217
|
-
* The
|
|
3215
|
+
* The things somebody is told once on a machine, and never again.
|
|
3216
|
+
*
|
|
3217
|
+
* Two of them now: the two lines of hello, and the disclosure that usage telemetry is on. They are
|
|
3218
|
+
* the same kind of fact and are kept the same way, so they share the marker helper at the bottom
|
|
3219
|
+
* rather than each growing their own copy of it.
|
|
3218
3220
|
*
|
|
3219
3221
|
* This began as a `postinstall` script, which is the obvious place for it and the wrong one: npm 11
|
|
3220
3222
|
* does not run install scripts unless they are allow-listed, so the banner would have reached
|
|
@@ -3231,84 +3233,78 @@ async function uninstall(command, deps = REAL) {
|
|
|
3231
3233
|
* as the telemetry notice next to it. A home directory that cannot be written to costs somebody a
|
|
3232
3234
|
* repeated banner and never a review, which is why nothing here throws.
|
|
3233
3235
|
*/
|
|
3234
|
-
var
|
|
3236
|
+
var GREETING = "onboarding-shown";
|
|
3237
|
+
var TELEMETRY_NOTICE = "telemetry-notice-shown";
|
|
3238
|
+
/** Whether this machine has been greeted. */
|
|
3239
|
+
function greeted() {
|
|
3240
|
+
return shown(GREETING);
|
|
3241
|
+
}
|
|
3242
|
+
function recordGreeting() {
|
|
3243
|
+
record(GREETING);
|
|
3244
|
+
}
|
|
3235
3245
|
/**
|
|
3236
|
-
* Whether this machine has been
|
|
3246
|
+
* Whether this machine has been told that usage telemetry is on.
|
|
3237
3247
|
*
|
|
3238
|
-
*
|
|
3239
|
-
*
|
|
3248
|
+
* A second marker rather than a reuse of the greeting: every machine installed before 0.30.0 was
|
|
3249
|
+
* greeted long ago and has still never been told about the default, so reading one from the other
|
|
3250
|
+
* would silence the disclosure for exactly the population it exists for.
|
|
3240
3251
|
*/
|
|
3241
|
-
function
|
|
3252
|
+
function telemetryNoticed() {
|
|
3253
|
+
return shown(TELEMETRY_NOTICE);
|
|
3254
|
+
}
|
|
3255
|
+
function recordTelemetryNotice() {
|
|
3256
|
+
record(TELEMETRY_NOTICE);
|
|
3257
|
+
}
|
|
3258
|
+
/**
|
|
3259
|
+
* Unreadable reads as already shown.
|
|
3260
|
+
*
|
|
3261
|
+
* Failing closed on repetition: the cost of this being wrong is a disclosure or a hello that never
|
|
3262
|
+
* arrives on a machine whose home cannot be read, and the cost of the other direction is one of
|
|
3263
|
+
* them printed over somebody's output every single time they run a command.
|
|
3264
|
+
*/
|
|
3265
|
+
function shown(name) {
|
|
3242
3266
|
try {
|
|
3243
|
-
return existsSync(
|
|
3267
|
+
return existsSync(join(STORE_ROOT, name));
|
|
3244
3268
|
} catch {
|
|
3245
3269
|
return true;
|
|
3246
3270
|
}
|
|
3247
3271
|
}
|
|
3248
|
-
function
|
|
3272
|
+
function record(name) {
|
|
3249
3273
|
try {
|
|
3250
3274
|
mkdirSync(STORE_ROOT, { recursive: true });
|
|
3251
|
-
writeFileSync(
|
|
3275
|
+
writeFileSync(join(STORE_ROOT, name), `${(/* @__PURE__ */ new Date()).toISOString()}\n`, { mode: 384 });
|
|
3252
3276
|
} catch {}
|
|
3253
3277
|
}
|
|
3254
3278
|
//#endregion
|
|
3255
|
-
//#region src/
|
|
3256
|
-
/**
|
|
3257
|
-
* The one question this product asks about itself, asked once per machine.
|
|
3258
|
-
*
|
|
3259
|
-
* `packages/review-harness/src/telemetry/` has recorded usage events to a local file since it
|
|
3260
|
-
* existed, disclosed and on by default. Sending a subset of them to a server we run is a different
|
|
3261
|
-
* act, and `apps/website/src/pages/Privacy.tsx` promised in as many words that if it ever happened
|
|
3262
|
-
* it would be opt in and off by default. This is how that promise is kept.
|
|
3263
|
-
*
|
|
3264
|
-
* Hand-rolled on `node:readline` beside `select.ts` and `confirm.ts`, for the reason they both
|
|
3265
|
-
* give: this binary has no runtime dependencies and one question is not worth acquiring one. The io
|
|
3266
|
-
* is injected for the same reason theirs is, so what the answer means is testable without a shell.
|
|
3267
|
-
*
|
|
3268
|
-
* **A bare Return re-asks rather than declining.** `confirm.ts` next door defaults to no on Return,
|
|
3269
|
-
* which is right there because every one of its callers is about to delete something. Here it would
|
|
3270
|
-
* be wrong in a subtler way: "no default" was the decision, and a capitalised `N` in the brackets
|
|
3271
|
-
* turns it into opt-out-by-keystroke, which is a different thing wearing the same words. Somebody
|
|
3272
|
-
* who has not read the question has not answered it.
|
|
3273
|
-
*
|
|
3274
|
-
* **No terminal means no question and no answer.** A durable job, a CI run and a piped invocation
|
|
3275
|
-
* must not block on a question nobody can see, and must not be recorded as having declined one:
|
|
3276
|
-
* the setting stays unset, nothing is sent, and the same machine is asked properly the first time
|
|
3277
|
-
* a person runs this interactively.
|
|
3278
|
-
*/
|
|
3279
|
-
async function askTelemetryUpload(options) {
|
|
3280
|
-
const stored = telemetryUploadConsent();
|
|
3281
|
-
if (stored !== null) return { kind: stored ? "granted" : "declined" };
|
|
3282
|
-
if (!options.isTTY) return { kind: "unanswered" };
|
|
3283
|
-
const reader = createInterface({ input: options.input });
|
|
3284
|
-
const lines = reader[Symbol.asyncIterator]();
|
|
3285
|
-
try {
|
|
3286
|
-
options.write(`${telemetryUploadQuestion()}\n`);
|
|
3287
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
3288
|
-
options.write("[y/n] ");
|
|
3289
|
-
const next = await lines.next();
|
|
3290
|
-
if (next.done) return { kind: "unanswered" };
|
|
3291
|
-
const answer = next.value.trim().toLowerCase();
|
|
3292
|
-
if (answer === "y" || answer === "yes") return settle(options, true);
|
|
3293
|
-
if (answer === "n" || answer === "no") return settle(options, false);
|
|
3294
|
-
options.write(telemetryUploadUnclear());
|
|
3295
|
-
}
|
|
3296
|
-
return { kind: "unanswered" };
|
|
3297
|
-
} finally {
|
|
3298
|
-
reader.close();
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3279
|
+
//#region src/telemetry_notice.ts
|
|
3301
3280
|
/**
|
|
3302
|
-
*
|
|
3281
|
+
* What this product says about itself, said once per machine.
|
|
3282
|
+
*
|
|
3283
|
+
* Until 0.30.0 this file's predecessor asked a question: nothing was transmitted until somebody
|
|
3284
|
+
* typed y, and `apps/website/src/pages/Privacy.tsx` promised in as many words that it would work
|
|
3285
|
+
* that way. The sample that produced was not a sample of users, it was a sample of people who
|
|
3286
|
+
* answer questions, and one or two installations reporting against hundreds of weekly downloads
|
|
3287
|
+
* answers none of the four questions the telemetry exists for. So the default changed, the promise
|
|
3288
|
+
* was rewritten rather than quietly edited, and the question became this.
|
|
3289
|
+
*
|
|
3290
|
+
* **A disclosure, not a question wearing different words.** No prompt, no keystroke, no default in
|
|
3291
|
+
* brackets. Asking anything at all, however phrased, goes back to measuring who answers.
|
|
3292
|
+
*
|
|
3293
|
+
* **Shown only where the new default is doing the work.** A machine that answered yes on an earlier
|
|
3294
|
+
* version learns nothing from being told; a machine that answered no would be told something false.
|
|
3295
|
+
* Both are left alone, which is why the claim everywhere else is "every installation whose
|
|
3296
|
+
* behaviour changes is told once" rather than "every machine is told".
|
|
3303
3297
|
*
|
|
3304
|
-
*
|
|
3305
|
-
*
|
|
3306
|
-
*
|
|
3298
|
+
* **No terminal check.** The old question could not be asked without one. A disclosure has nobody
|
|
3299
|
+
* to wait for, and a headless install that transmits is exactly the install that must not be the
|
|
3300
|
+
* one nobody ever told. It costs a CI log three lines on a persisted home, once.
|
|
3307
3301
|
*/
|
|
3308
|
-
function
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3302
|
+
function noticeTelemetry(write) {
|
|
3303
|
+
if (process.env.PRB_TELEMETRY === "off") return;
|
|
3304
|
+
if (telemetryUploadConsent() !== null) return;
|
|
3305
|
+
if (telemetryNoticed()) return;
|
|
3306
|
+
write(telemetryNotice());
|
|
3307
|
+
recordTelemetryNotice();
|
|
3312
3308
|
}
|
|
3313
3309
|
//#endregion
|
|
3314
3310
|
//#region src/main.ts
|
|
@@ -3344,32 +3340,30 @@ function greet(command) {
|
|
|
3344
3340
|
recordGreeting();
|
|
3345
3341
|
}
|
|
3346
3342
|
/**
|
|
3347
|
-
* The one
|
|
3343
|
+
* The one-time telemetry disclosure, printed after the hello and before the work.
|
|
3348
3344
|
*
|
|
3349
|
-
* The greeting's commands minus `config`, which is where the
|
|
3350
|
-
* `config set telemetry-upload off`
|
|
3351
|
-
*
|
|
3345
|
+
* The greeting's commands minus `config`, which is where the setting is changed: somebody typing
|
|
3346
|
+
* `config set telemetry-upload off` should be able to turn it off without being told first that it
|
|
3347
|
+
* is on. That was true when this was a question and it is still true now it is a notice.
|
|
3352
3348
|
*
|
|
3353
|
-
* On stderr
|
|
3354
|
-
*
|
|
3349
|
+
* On stderr, like every other line here that is not a result, so `prreviewbuddy review | pbcopy`
|
|
3350
|
+
* still copies a URL. Unlike the question it replaced it takes no input and checks for no terminal:
|
|
3351
|
+
* there is nobody to wait for, and a headless install that transmits is the one that most needs
|
|
3352
|
+
* telling.
|
|
3355
3353
|
*/
|
|
3356
|
-
var
|
|
3354
|
+
var NOTIFIED = [
|
|
3357
3355
|
"review",
|
|
3358
3356
|
"open",
|
|
3359
3357
|
"agents"
|
|
3360
3358
|
];
|
|
3361
|
-
|
|
3362
|
-
if (!
|
|
3363
|
-
|
|
3364
|
-
isTTY: Boolean(process.stdin.isTTY && process.stderr.isTTY),
|
|
3365
|
-
input: process.stdin,
|
|
3366
|
-
write: (chunk) => void process.stderr.write(chunk)
|
|
3367
|
-
});
|
|
3359
|
+
function notify(command) {
|
|
3360
|
+
if (!NOTIFIED.includes(command.name)) return;
|
|
3361
|
+
noticeTelemetry((chunk) => void process.stderr.write(chunk));
|
|
3368
3362
|
}
|
|
3369
3363
|
async function main(argv) {
|
|
3370
3364
|
const command = parse(argv);
|
|
3371
3365
|
greet(command);
|
|
3372
|
-
|
|
3366
|
+
notify(command);
|
|
3373
3367
|
switch (command.name) {
|
|
3374
3368
|
case "help":
|
|
3375
3369
|
out(HELP);
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as fillFileUrlTemplate, $t as writeAgentPreference, A as removeWorktree, At as workspaceRevision, Bt as isQuestionOutstanding, C as recordTelemetryUploadConsent, Ct as previousKissResult, D as ReviewBeingDeletedError, Dt as saveWorkspace, E as EXPLAIN_SIMPLY_PROMPT, F as clearServerRecord, Ft as queueStamp, G as DEFAULT_WORKSPACE_PORT, Gt as describeAuthorship, H as BUILD_VERSION, Ht as questionsOutstanding, It as readQueue, J as parseWorkspacePort, Jt as git, K as MAX_WORKSPACE_PORT, L as readServerRecord, Lt as writeQueue, M as relativeTime$1, Mt as dequeueOnComplete, N as readIndexToken, Nt as queueAdd, O as MANAGED_ROOT, Pt as queueRemove, Qt as readAgentPreference, Rt as doneVerb, S as record, T as withUsageRecorded, Tt as reviewedCommit, Ut as reviewerDispositions, V as writeServerRecord, Vt as issuesOutstanding, W as feedbackUrl, Wt as processDiscussion, X as statedWorkspacePort, Xt as resolveModel, Y as resolveWorkspacePort, Z as writePortPreference, Zt as clearAgentPreference, _ as startKissJob, _t as latestKissRun, a as wasBlocked, an as agentFor, at as isTerminal, bt as loadWorkspace, c as updateReview, cn as AgentUnavailableError, d as askCheckout, dt as purposeOf, et as checkCodeFreshness, f as UpdateAlreadyRunningError, gt as indexReviewGroups, h as agentEnvOf, i as liveJobsFor, it as fail, jt as workspaceStamps, k as readMarker, kt as touchWorkspace, l as refreshPrContext, lt as phasesFor, mt as followedRefName, n as deleteReview, nn as AGENT_IDS, nt as allJobs, o as discardJob, ot as loadJob, p as liveUpdateFor, pt as isClaimed, q as MIN_WORKSPACE_PORT, r as lineageIds, rt as endedAt, s as RefreshUnavailableError, sn as AgentCancelledError, st as saveJob, t as deleteLineage, tn as STORE_ROOT, tt as checkFreshness, u as isUnchanged, ut as progressSteps, w as telemetryUploadConsent, wt as recentWorkspaces, x as readEvents, y as runJob, yt as lineagePosition, zt as isIssueOutstanding } from "./delete_review-
|
|
1
|
+
import { $ as fillFileUrlTemplate, $t as writeAgentPreference, A as removeWorktree, At as workspaceRevision, Bt as isQuestionOutstanding, C as recordTelemetryUploadConsent, Ct as previousKissResult, D as ReviewBeingDeletedError, Dt as saveWorkspace, E as EXPLAIN_SIMPLY_PROMPT, F as clearServerRecord, Ft as queueStamp, G as DEFAULT_WORKSPACE_PORT, Gt as describeAuthorship, H as BUILD_VERSION, Ht as questionsOutstanding, It as readQueue, J as parseWorkspacePort, Jt as git, K as MAX_WORKSPACE_PORT, L as readServerRecord, Lt as writeQueue, M as relativeTime$1, Mt as dequeueOnComplete, N as readIndexToken, Nt as queueAdd, O as MANAGED_ROOT, Pt as queueRemove, Qt as readAgentPreference, Rt as doneVerb, S as record, T as withUsageRecorded, Tt as reviewedCommit, Ut as reviewerDispositions, V as writeServerRecord, Vt as issuesOutstanding, W as feedbackUrl, Wt as processDiscussion, X as statedWorkspacePort, Xt as resolveModel, Y as resolveWorkspacePort, Z as writePortPreference, Zt as clearAgentPreference, _ as startKissJob, _t as latestKissRun, a as wasBlocked, an as agentFor, at as isTerminal, bt as loadWorkspace, c as updateReview, cn as AgentUnavailableError, d as askCheckout, dt as purposeOf, et as checkCodeFreshness, f as UpdateAlreadyRunningError, gt as indexReviewGroups, h as agentEnvOf, i as liveJobsFor, it as fail, jt as workspaceStamps, k as readMarker, kt as touchWorkspace, l as refreshPrContext, lt as phasesFor, mt as followedRefName, n as deleteReview, nn as AGENT_IDS, nt as allJobs, o as discardJob, ot as loadJob, p as liveUpdateFor, pt as isClaimed, q as MIN_WORKSPACE_PORT, r as lineageIds, rt as endedAt, s as RefreshUnavailableError, sn as AgentCancelledError, st as saveJob, t as deleteLineage, tn as STORE_ROOT, tt as checkFreshness, u as isUnchanged, ut as progressSteps, w as telemetryUploadConsent, wt as recentWorkspaces, x as readEvents, y as runJob, yt as lineagePosition, zt as isIssueOutstanding } from "./delete_review-B_2J2Wn9.js";
|
|
2
2
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
@@ -5058,27 +5058,31 @@ function renderReviewsSection(settings) {
|
|
|
5058
5058
|
</section>`;
|
|
5059
5059
|
}
|
|
5060
5060
|
/**
|
|
5061
|
-
* The telemetry
|
|
5061
|
+
* The telemetry setting, visible.
|
|
5062
5062
|
*
|
|
5063
5063
|
* It has always been changeable, and only from the command line, which is a poor place for the one
|
|
5064
|
-
* setting a person is most likely to want to check rather than to set.
|
|
5065
|
-
*
|
|
5066
|
-
*
|
|
5064
|
+
* setting a person is most likely to want to check rather than to set.
|
|
5065
|
+
*
|
|
5066
|
+
* Two radios over three stored states. Until 0.30.0 the third state meant nothing was being sent,
|
|
5067
|
+
* so neither radio could be checked without answering on somebody's behalf. Now it means the
|
|
5068
|
+
* default is applying and the default is on, so Send it is checked and the help line says which of
|
|
5069
|
+
* the two reasons it is on for. The storage keeps all three, because a stored no has to outrank any
|
|
5070
|
+
* future change of default.
|
|
5067
5071
|
*/
|
|
5068
5072
|
function renderPrivacySection(settings) {
|
|
5069
|
-
const on = settings.usageCounts === true;
|
|
5070
5073
|
const off = settings.usageCounts === false;
|
|
5071
5074
|
return `<section class="prb-settings-section">
|
|
5072
5075
|
<h2>Privacy</h2>
|
|
5073
5076
|
<div class="prb-field prb-settings-field" role="group" aria-labelledby="prb-usage-label" aria-describedby="prb-usage-help">
|
|
5074
|
-
<span id="prb-usage-label">
|
|
5077
|
+
<span id="prb-usage-label">Non-identifying usage telemetry</span>
|
|
5075
5078
|
<div class="prb-settings-choice">
|
|
5076
|
-
<label><input type="radio" name="prb-usage" value="on" data-settings-usage${
|
|
5077
|
-
<label><input type="radio" name="prb-usage" value="off" data-settings-usage${off ? " checked" : ""}> Do not send
|
|
5079
|
+
<label><input type="radio" name="prb-usage" value="on" data-settings-usage${off ? "" : " checked"}> Send it</label>
|
|
5080
|
+
<label><input type="radio" name="prb-usage" value="off" data-settings-usage${off ? " checked" : ""}> Do not send it</label>
|
|
5078
5081
|
</div>
|
|
5079
5082
|
</div>
|
|
5080
|
-
<p class="prb-settings-help" id="prb-usage-help">
|
|
5081
|
-
|
|
5083
|
+
<p class="prb-settings-help" id="prb-usage-help">When a review starts and finishes, with the
|
|
5084
|
+
version, platform, agent, whether it succeeded and how long it took. Never your code, your diffs,
|
|
5085
|
+
your questions or the names of anything you review.${settings.usageCounts === null ? " It is on by default; you have not changed it." : ""}</p>
|
|
5082
5086
|
</section>`;
|
|
5083
5087
|
}
|
|
5084
5088
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prreviewbuddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Review a branch or pull request in an isolated checkout, from your terminal, with the coding agent you already have installed.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://prreviewbuddy.com",
|