prreviewbuddy 0.62.0 → 0.63.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 +17 -0
- package/dist/{delete_review-LzUgEUzN.js → delete_review-CeJo9t7h.js} +64 -17
- package/dist/main.js +147 -19
- package/dist/server.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.63.0
|
|
4
|
+
|
|
5
|
+
**Telemetry now records first use.** With telemetry on, PR Review Buddy sends one more event,
|
|
6
|
+
`installation_started`, once per installation: the first time you run `prreviewbuddy` on its own,
|
|
7
|
+
`review`, `open` or `agents`. It marks first use, not when the package was installed, and it adds
|
|
8
|
+
no field. It carries the installation id, version, platform and a timestamp, and no agent. If it
|
|
9
|
+
cannot reach the server it is tried again on your next such command, and never again once it
|
|
10
|
+
arrives. The server keeps one per installation, so a repeat is acknowledged and not stored. Until now only reviews were counted, so somebody who tried PR Review Buddy and stopped
|
|
11
|
+
before a review was invisible. Installations from before this version send it once, on their first
|
|
12
|
+
command after upgrading.
|
|
13
|
+
|
|
14
|
+
`prreviewbuddy` on its own now shows the one-time telemetry notice too, because it can now send.
|
|
15
|
+
Its usage text is unchanged. An installation that already saw the telemetry notice, or turned telemetry
|
|
16
|
+
on, is told once about this one addition; one that turned it off is told nothing. `--help`, `--version` and `config` send nothing and show no notice.
|
|
17
|
+
`prreviewbuddy config set telemetry-upload off` and `PRB_TELEMETRY=off` stop this event exactly as
|
|
18
|
+
they stop the others. The privacy page lists it: https://prreviewbuddy.com/privacy
|
|
19
|
+
|
|
3
20
|
## 0.62.0
|
|
4
21
|
|
|
5
22
|
**Buddy AI has its own keys.** On a review, ⌘K B opens a Buddy AI menu. B O opens Buddy AI with
|
|
@@ -13586,6 +13586,13 @@ var TIMEOUT_MS = 1500;
|
|
|
13586
13586
|
*/
|
|
13587
13587
|
function project(record) {
|
|
13588
13588
|
const event = record.event;
|
|
13589
|
+
if (event.event === "installation_started") return {
|
|
13590
|
+
event: event.event,
|
|
13591
|
+
installationId: record.anonymousId,
|
|
13592
|
+
cliVersion: record.cliVersion,
|
|
13593
|
+
platform: record.platform,
|
|
13594
|
+
occurredAt: record.at
|
|
13595
|
+
};
|
|
13589
13596
|
if (event.event !== "review_started" && event.event !== "review_finished") return null;
|
|
13590
13597
|
const base = {
|
|
13591
13598
|
event: event.event,
|
|
@@ -13614,13 +13621,38 @@ function send(record) {
|
|
|
13614
13621
|
if (!telemetryUploadEnabled()) return;
|
|
13615
13622
|
const payload = project(record);
|
|
13616
13623
|
if (!payload) return;
|
|
13617
|
-
|
|
13624
|
+
post(payload);
|
|
13625
|
+
} catch {}
|
|
13626
|
+
}
|
|
13627
|
+
/**
|
|
13628
|
+
* Send one record and say whether the server took it. Never rejects.
|
|
13629
|
+
*
|
|
13630
|
+
* For the one caller that has to know: `installation_started` is sent until it lands, so it needs
|
|
13631
|
+
* a yes it can trust, which is a 2xx and nothing else. Asks the same question `send` does before
|
|
13632
|
+
* anything goes over the wire.
|
|
13633
|
+
*/
|
|
13634
|
+
async function deliver(record) {
|
|
13635
|
+
try {
|
|
13636
|
+
if (!telemetryUploadEnabled()) return false;
|
|
13637
|
+
const payload = project(record);
|
|
13638
|
+
return payload ? await post(payload) : false;
|
|
13639
|
+
} catch {
|
|
13640
|
+
return false;
|
|
13641
|
+
}
|
|
13642
|
+
}
|
|
13643
|
+
async function post(payload) {
|
|
13644
|
+
try {
|
|
13645
|
+
const response = await fetch(endpoint(), {
|
|
13618
13646
|
method: "POST",
|
|
13619
13647
|
headers: { "content-type": "application/json" },
|
|
13620
13648
|
body: JSON.stringify(payload),
|
|
13621
13649
|
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
13622
|
-
})
|
|
13623
|
-
|
|
13650
|
+
});
|
|
13651
|
+
await response.body?.cancel().catch(() => {});
|
|
13652
|
+
return response.ok;
|
|
13653
|
+
} catch {
|
|
13654
|
+
return false;
|
|
13655
|
+
}
|
|
13624
13656
|
}
|
|
13625
13657
|
/**
|
|
13626
13658
|
* Where to. Overridable so the endpoint can be pointed at a local server while it is being built,
|
|
@@ -13645,29 +13677,44 @@ var LOG_PATH = join(STORE_ROOT, "events.jsonl");
|
|
|
13645
13677
|
* The gate is on **transmission only**. Writing a file the developer owns and can read, delete or
|
|
13646
13678
|
* disable is a different act from sending to a server, and the consent that suits one does not
|
|
13647
13679
|
* carry to the other, so `events.jsonl` keeps recording the full event union either way and the
|
|
13648
|
-
* question asked on first run is about the wire.
|
|
13680
|
+
* question asked on first run is about the wire. Twenty events are written here; three of them,
|
|
13649
13681
|
* carrying eight fields between them, are the most that is ever sent.
|
|
13650
13682
|
*/
|
|
13651
13683
|
function record(event) {
|
|
13652
13684
|
try {
|
|
13653
|
-
const
|
|
13654
|
-
if (!
|
|
13655
|
-
|
|
13656
|
-
anonymousId: config.anonymousId,
|
|
13657
|
-
cliVersion: BUILD_VERSION,
|
|
13658
|
-
platform: platformName(),
|
|
13659
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13660
|
-
event
|
|
13661
|
-
};
|
|
13662
|
-
mkdirSync(STORE_ROOT, { recursive: true });
|
|
13663
|
-
appendFileSync(LOG_PATH, `${JSON.stringify(line)}\n`, { mode: 384 });
|
|
13685
|
+
const line = stamp(event);
|
|
13686
|
+
if (!line) return;
|
|
13687
|
+
append(line);
|
|
13664
13688
|
send(line);
|
|
13665
13689
|
} catch {}
|
|
13666
13690
|
}
|
|
13691
|
+
/**
|
|
13692
|
+
* The line an event would be logged as, or `null` when the log is switched off.
|
|
13693
|
+
*
|
|
13694
|
+
* Split from `record` for `installation_started`, which is built and sent first and logged only
|
|
13695
|
+
* once the server has it: logging each attempt would leave one line per failed try for an event
|
|
13696
|
+
* that happens once.
|
|
13697
|
+
*/
|
|
13698
|
+
function stamp(event) {
|
|
13699
|
+
const config = loadConfig();
|
|
13700
|
+
if (!config.telemetry) return null;
|
|
13701
|
+
return {
|
|
13702
|
+
anonymousId: config.anonymousId,
|
|
13703
|
+
cliVersion: BUILD_VERSION,
|
|
13704
|
+
platform: platformName(),
|
|
13705
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13706
|
+
event
|
|
13707
|
+
};
|
|
13708
|
+
}
|
|
13709
|
+
/** Write one line to the log. Throws on a failed write; callers decide what that costs. */
|
|
13710
|
+
function append(line) {
|
|
13711
|
+
mkdirSync(STORE_ROOT, { recursive: true });
|
|
13712
|
+
appendFileSync(LOG_PATH, `${JSON.stringify(line)}\n`, { mode: 384 });
|
|
13713
|
+
}
|
|
13667
13714
|
/** This review's own events, oldest first. The only reader, used to derive the rollup. */
|
|
13668
13715
|
function readEvents(reviewId) {
|
|
13669
13716
|
try {
|
|
13670
|
-
return readFileSync(LOG_PATH, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line)).
|
|
13717
|
+
return readFileSync(LOG_PATH, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line)).map((entry) => entry.event).filter((event) => event !== void 0 && "reviewId" in event && event.reviewId === reviewId);
|
|
13671
13718
|
} catch {
|
|
13672
13719
|
return [];
|
|
13673
13720
|
}
|
|
@@ -16152,4 +16199,4 @@ function lineageIds(id) {
|
|
|
16152
16199
|
return [.../* @__PURE__ */ new Set([id, ...lineage.map((review) => review.id)])];
|
|
16153
16200
|
}
|
|
16154
16201
|
//#endregion
|
|
16155
|
-
export {
|
|
16202
|
+
export { storedScopes as $, loadWorkspace as $n, detectAgents as $r, bootstrapUrl as $t, reanalyseReview as A, endedAt as An, issuesOutstanding as Ar, renderMarkdown as At, telemetryUploadEnabled as B, clearClaim as Bn, isCommitReview as Br, createWorktree as Bt, track as C, remoteHost as Cn, queueAddMany as Cr, unreadCount as Ct, endRun as D, followedBranch as Dn, doneVerb as Dr, authorPreview as Dt, completeRun as E, checkReview as En, readQueue as Er, UNESTABLISHED_MOTIVATION as Et, record as F, PHASES as Fn, describeAuthorship as Fr, asString as Ft, describeRuntimes as G, groupByLineage as Gn, readAgentPreference as Gr, requestJobStop as Gt, mapRuntimes as H, isClaimed as Hn, resolveAgent as Hr, isWorktreeHeld as Ht, stamp as I, isLiveJob as In, WITHHELD_REASON as Ir, extractJsonText as It, loadArchitecture as J, kissDecisionRefusal as Jn, STORE_ROOT as Jr, activateQueued as Jt, ARCHITECTURE_DIR as K, indexKeptIds as Kn, writeAgentPreference as Kr, modelHelpLines as Kt, deliver as L, phasesFor as Ln, kissOutcome as Lr, objectWithAnyOf as Lt, startJob as M, isTerminal as Mn, reviewerDispositions as Mr, CODE_COPY_CLASS as Mt, append as N, loadJob as Nn, processDiscussion as Nr, escapeHtml as Nt, reportRun as O, reviewedShaOf as On, isIssueOutstanding as Or, hasAuthorContent as Ot, readEvents as P, saveJob as Pn, recommendationsOf as Pr, renderInlineText as Pt, storeArchitecture as Q, lineagePosition as Qn, agentFor as Qr, readIndexToken as Qt, recordTelemetryUploadConsent as R, progressSteps as Rn, resolveTarget as Rr, ReviewBeingDeletedError as Rt, isDriving as S, forgeFor as Sn, queueAdd as Sr, toneOf as St, beginRun as T, checkCodeFreshness as Tn, queueStamp as Tr, EXPLAIN_SIMPLY_PROMPT as Tt, recordRuntimeBestEffort as U, decideKissRecommendation as Un, resolveModel as Ur, readMarker as Ut, waitingHistory as V, driverGone as Vn, git as Vr, holdWorktree as Vt, RUNTIME_WORDS as W, followedRefName as Wn, clearAgentPreference as Wr, removeWorktree as Wt, readArchitecture as X, latestKissRun as Xn, DEFAULT_AGENT_ID as Xr, queueForReview as Xt, partFiles as Y, kissDecisionsOf as Yn, AGENT_IDS as Yr, compactQueue as Yt, repositoryKey as Z, lineageKeyFor as Zn, agentById as Zr, settleQueue as Zt, liveUpdateFor as _, resolveWorkspacePort as _n, lifecycleItemLabel as _r, markRead as _t, wasBlocked as a, spawnServer as an, recentWorkspaces as ar, isArchitectureId as at, recordKissRun as b, claimRequestUrl as bn, parseLifecycle as br, readNotification as bt, RefreshUnavailableError as c, workspaceUrl as cn, summarise as cr, branchOf as ct, mergeUpdateInto as d, PACKAGE_NAME as dn, workspaceStamps as dr, createPrState as dt, AgentCancelledError as ei, clearServerRecordOwnedBy as en, matchingWorkspaceIds as er, trackedFiles as et, updateReview as f, feedbackUrl as fn, LIFECYCLES as fr, createPrUpdate as ft, UpdateAlreadyRunningError as g, parseWorkspacePort as gn, lifecycleChip as gr, markAllRead as gt, askCheckout as h, MIN_WORKSPACE_PORT as hn, isLifecycle as hr, listNotifications as ht, liveJobsFor as i, serverEntryExists as in, previousKissResult as ir, ancestorsOf as it, runJob as j, fail as jn, questionsOutstanding as jr, CODE_BLOCK_CLASS as jt, stopRun as k, allJobs as kn, isQuestionOutstanding as kr, GITHUB_MARKDOWN as kt, cancelUpdate as l, writeServerRecord as ln, touchWorkspace as lr, subjectOf as lt, isUnchanged as m, MAX_WORKSPACE_PORT as mn, activityLabel as mr, headline as mt, deleteReview as n, readServerRecord as nn, positionInLineage as nr, QUOTE_LIMITS as nt, discardJob as o, stopChild as on, reviewedCommit as or, writeJsonAtomically as ot, refreshPrContext as p, DEFAULT_WORKSPACE_PORT as pn, activityAttributes as pr, eventName as pt, checkArchitecture as q, indexReviewGroups as qn, CONFIG_PATH as qr, relativeTime as qt, lineageIds as r, reviewsUrl as rn, presentedKissRunOf as rr, REPOSITORY_SCOPE as rt, FETCH_TIMEOUT_MS as s, stopServer as sn, reviewedRepositories as sr, announceJob as st, deleteLineage as t, AgentUnavailableError as ti, ensureServer as tn, mutateWorkspace as tr, ownersOf as tt, cancellableHere as u, BUILD_VERSION as un, workspaceRevision as ur, createArchitectureNotification as ut, runningJobs as v, statedWorkspacePort as vn, lifecyclePill as vr, markReadForReview as vt, RUN_KINDS as w, fillFileUrlTemplate as wn, queueRemove as wr, withUsageRecorded as wt, startKissJob as x, forgeResolver as xn, sectionOf as xr, removeNotification as xt, agentEnvOf as y, writePortPreference as yn, offersLifecycle as yr, pruneNotifications as yt, telemetryUploadConsent as z, purposeOf as zn, displayRef as zr, MANAGED_ROOT as zt };
|
package/dist/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $t as
|
|
2
|
+
import { $n as loadWorkspace, $r as detectAgents, $t as bootstrapUrl, A as reanalyseReview, B as telemetryUploadEnabled, Bn as clearClaim, En as checkReview, F as record$1, Fn as PHASES, Gn as groupByLineage, Gr as readAgentPreference, Hn as isClaimed, Hr as resolveAgent, I as stamp, Ir as WITHHELD_REASON, Jr as STORE_ROOT, Kr as writeAgentPreference, Kt as modelHelpLines, L as deliver, Lr as kissOutcome, M as startJob, Mn as isTerminal, N as append, Nn as loadJob, Qt as readIndexToken, R as recordTelemetryUploadConsent, Rn as progressSteps, Rr as resolveTarget, Ur as resolveModel, Ut as readMarker, Vn as driverGone, Vr as git, Wn as followedRefName, Wt as removeWorktree, Xr as DEFAULT_AGENT_ID, Xt as queueForReview, Yr as AGENT_IDS, Zn as lineageKeyFor, Zr as agentById, _ as liveUpdateFor, a as wasBlocked, ar as recentWorkspaces, b as recordKissRun, cn as workspaceUrl, cr as summarise, d as mergeUpdateInto, dn as PACKAGE_NAME, er as matchingWorkspaceIds, f as updateReview, g as UpdateAlreadyRunningError, gn as parseWorkspacePort, hn as MIN_WORKSPACE_PORT, i as liveJobsFor, j as runJob, kn as allJobs, mn as MAX_WORKSPACE_PORT, n as deleteReview$1, nr as positionInLineage, pn as DEFAULT_WORKSPACE_PORT, qr as CONFIG_PATH, qt as relativeTime, rn as reviewsUrl, sn as stopServer, sr as reviewedRepositories, tn as ensureServer, tr as mutateWorkspace, un as BUILD_VERSION, v as runningJobs, vn as statedWorkspacePort, xn as forgeResolver, yn as writePortPreference, z as telemetryUploadConsent, zn as purposeOf, zr as displayRef, zt as MANAGED_ROOT } from "./delete_review-CeJo9t7h.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";
|
|
@@ -107,15 +107,24 @@ var AUDIENCES = {
|
|
|
107
107
|
reviewer: "writes it for somebody reviewing somebody else's work, with whether each one is worth raising"
|
|
108
108
|
};
|
|
109
109
|
function parse(argv) {
|
|
110
|
-
if (argv.length === 0) return {
|
|
110
|
+
if (argv.length === 0) return {
|
|
111
|
+
name: "help",
|
|
112
|
+
via: "bare"
|
|
113
|
+
};
|
|
111
114
|
const [head, ...rest] = argv;
|
|
112
|
-
if (head === "--help" || head === "-h" || head === "help") return {
|
|
115
|
+
if (head === "--help" || head === "-h" || head === "help") return {
|
|
116
|
+
name: "help",
|
|
117
|
+
via: "asked"
|
|
118
|
+
};
|
|
113
119
|
if (head === "--version" || head === "-v") return { name: "version" };
|
|
114
120
|
if (head === void 0 || !(head in VALUED)) return {
|
|
115
121
|
name: "error",
|
|
116
122
|
message: unknownCommand(head)
|
|
117
123
|
};
|
|
118
|
-
if (rest.includes("--help") || rest.includes("-h")) return {
|
|
124
|
+
if (rest.includes("--help") || rest.includes("-h")) return {
|
|
125
|
+
name: "help",
|
|
126
|
+
via: "asked"
|
|
127
|
+
};
|
|
119
128
|
const values = {};
|
|
120
129
|
const flags = /* @__PURE__ */ new Set();
|
|
121
130
|
const positional = [];
|
|
@@ -1217,12 +1226,26 @@ function unknownSetting(key, known) {
|
|
|
1217
1226
|
* little else is attached to it. "Anonymous" is a word with a technical meaning strong enough to be
|
|
1218
1227
|
* challenged on, and the claim below does not need it to be true.
|
|
1219
1228
|
*/
|
|
1229
|
+
/**
|
|
1230
|
+
* The one addition since the notice an installation already saw, or the yes it already gave. Not the
|
|
1231
|
+
* notice again: repeating what somebody was told reads as something else having changed.
|
|
1232
|
+
*/
|
|
1233
|
+
function telemetryFirstUseNotice() {
|
|
1234
|
+
return [
|
|
1235
|
+
"",
|
|
1236
|
+
"Usage telemetry now also records first use: one event, once per installation, with the",
|
|
1237
|
+
"same version, platform and timestamp fields as before. Nothing else changed.",
|
|
1238
|
+
"Turn it off: `prreviewbuddy config set telemetry-upload off`",
|
|
1239
|
+
"Details: https://prreviewbuddy.com/privacy",
|
|
1240
|
+
""
|
|
1241
|
+
].join("\n");
|
|
1242
|
+
}
|
|
1220
1243
|
function telemetryNotice() {
|
|
1221
1244
|
return [
|
|
1222
1245
|
"",
|
|
1223
|
-
"Non-identifying usage telemetry is on by default: review start and finish,
|
|
1224
|
-
"platform, agent, success and duration. Never your code, diffs, repository
|
|
1225
|
-
"review content.",
|
|
1246
|
+
"Non-identifying usage telemetry is on by default: first use, review start and finish,",
|
|
1247
|
+
"version, platform, agent, success and duration. Never your code, diffs, repository",
|
|
1248
|
+
"names or review content.",
|
|
1226
1249
|
"Turn it off: `prreviewbuddy config set telemetry-upload off`",
|
|
1227
1250
|
"Details: https://prreviewbuddy.com/privacy",
|
|
1228
1251
|
""
|
|
@@ -3413,6 +3436,7 @@ async function uninstall(command, deps = REAL) {
|
|
|
3413
3436
|
*/
|
|
3414
3437
|
var GREETING = "onboarding-shown";
|
|
3415
3438
|
var TELEMETRY_NOTICE = "telemetry-notice-shown";
|
|
3439
|
+
var FIRST_USE_NOTICE = "telemetry-first-use-notice-shown";
|
|
3416
3440
|
/** Whether this machine has been greeted. */
|
|
3417
3441
|
function greeted() {
|
|
3418
3442
|
return shown(GREETING);
|
|
@@ -3433,6 +3457,13 @@ function telemetryNoticed() {
|
|
|
3433
3457
|
function recordTelemetryNotice() {
|
|
3434
3458
|
record(TELEMETRY_NOTICE);
|
|
3435
3459
|
}
|
|
3460
|
+
/** Whether this machine has been told that 0.63.0 added `installation_started` to what is sent. */
|
|
3461
|
+
function firstUseNoticed() {
|
|
3462
|
+
return shown(FIRST_USE_NOTICE);
|
|
3463
|
+
}
|
|
3464
|
+
function recordFirstUseNotice() {
|
|
3465
|
+
record(FIRST_USE_NOTICE);
|
|
3466
|
+
}
|
|
3436
3467
|
/**
|
|
3437
3468
|
* Unreadable reads as already shown.
|
|
3438
3469
|
*
|
|
@@ -3473,16 +3504,108 @@ function record(name) {
|
|
|
3473
3504
|
* Both are left alone, which is why the claim everywhere else is "every installation whose
|
|
3474
3505
|
* behaviour changes is told once" rather than "every machine is told".
|
|
3475
3506
|
*
|
|
3507
|
+
* **0.63.0 is such a change for everybody who sends.** `installation_started` joined what leaves
|
|
3508
|
+
* the machine, so an installation told the old list, or that said yes to it, is told once about the
|
|
3509
|
+
* addition, under its own marker. A no is still told nothing.
|
|
3510
|
+
*
|
|
3476
3511
|
* **No terminal check.** The old question could not be asked without one. A disclosure has nobody
|
|
3477
3512
|
* to wait for, and a headless install that transmits is exactly the install that must not be the
|
|
3478
3513
|
* one nobody ever told. It costs a CI log three lines on a persisted home, once.
|
|
3479
3514
|
*/
|
|
3480
3515
|
function noticeTelemetry(write) {
|
|
3481
3516
|
if (process.env.PRB_TELEMETRY === "off") return;
|
|
3482
|
-
|
|
3483
|
-
if (
|
|
3484
|
-
|
|
3485
|
-
|
|
3517
|
+
const consent = telemetryUploadConsent();
|
|
3518
|
+
if (consent === false) return;
|
|
3519
|
+
if (consent === null && !telemetryNoticed()) {
|
|
3520
|
+
write(telemetryNotice());
|
|
3521
|
+
recordTelemetryNotice();
|
|
3522
|
+
recordFirstUseNotice();
|
|
3523
|
+
return;
|
|
3524
|
+
}
|
|
3525
|
+
if (firstUseNoticed()) return;
|
|
3526
|
+
write(telemetryFirstUseNotice());
|
|
3527
|
+
recordFirstUseNotice();
|
|
3528
|
+
}
|
|
3529
|
+
//#endregion
|
|
3530
|
+
//#region src/product_commands.ts
|
|
3531
|
+
/**
|
|
3532
|
+
* Whether this command is somebody using the product, rather than asking about the binary or
|
|
3533
|
+
* changing a setting.
|
|
3534
|
+
*
|
|
3535
|
+
* One answer for two things, on purpose. These commands print the one-time telemetry notice, and
|
|
3536
|
+
* they are the commands that may send `installation_started`. If the lists ever differed, a
|
|
3537
|
+
* command could transmit before the installation had been told telemetry is on.
|
|
3538
|
+
*
|
|
3539
|
+
* `prreviewbuddy` on its own counts: it is somebody entering the product to see what it does.
|
|
3540
|
+
* `--help` and `--version` do not, nor `config` (where the setting is changed, and turning it off
|
|
3541
|
+
* must not need a disclosure first) or `uninstall`.
|
|
3542
|
+
*/
|
|
3543
|
+
function isProductCommand(command) {
|
|
3544
|
+
switch (command.name) {
|
|
3545
|
+
case "review":
|
|
3546
|
+
case "open":
|
|
3547
|
+
case "agents": return true;
|
|
3548
|
+
case "help": return command.via === "bare";
|
|
3549
|
+
default: return false;
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
//#endregion
|
|
3553
|
+
//#region ../../packages/review-harness/src/telemetry/installation_started.ts
|
|
3554
|
+
/**
|
|
3555
|
+
* Tell the server, once, that this installation was used.
|
|
3556
|
+
*
|
|
3557
|
+
* The review events only fire from inside a review job, so an installation that was tried and
|
|
3558
|
+
* left before its first review was invisible: nothing told "nobody installs this" apart from
|
|
3559
|
+
* "people install it and stop before reviewing". This is the event that sits in front of them.
|
|
3560
|
+
*
|
|
3561
|
+
* It means first meaningful use that telemetry saw, and the name has to survive that reading:
|
|
3562
|
+
* an installation from before the event existed sends it on its first command after upgrading,
|
|
3563
|
+
* and nothing on this machine knows when the package was actually installed.
|
|
3564
|
+
*
|
|
3565
|
+
* **Sent until it lands.** Every other event is best effort, because losing one review out of a
|
|
3566
|
+
* thousand changes nothing. Losing this one loses the installation from the funnel, so the marker
|
|
3567
|
+
* is written only after a 2xx, and every meaningful command until then tries again. Any failure
|
|
3568
|
+
* retries, a 422 included: a CLI that reached somebody before the API learned the event recovers
|
|
3569
|
+
* on its own once it has. `occurredAt` is the attempt's own time, never a remembered first try,
|
|
3570
|
+
* because the server refuses a timestamp more than two days from when it arrives and a stored
|
|
3571
|
+
* one would be refused forever.
|
|
3572
|
+
*
|
|
3573
|
+
* **Once is kept by the server, not here.** This side cannot know that a request it saw fail was
|
|
3574
|
+
* not stored: an acknowledgement lost after the row committed looks exactly like a failure, and two
|
|
3575
|
+
* commands started together on one home both pass the marker check before either has written it.
|
|
3576
|
+
* Both resend. The API holds one `installation_started` per installation under a unique index and
|
|
3577
|
+
* answers a repeat with a 200 it does not store, which also stops the retrying. The marker only
|
|
3578
|
+
* saves requests.
|
|
3579
|
+
*/
|
|
3580
|
+
var MARKER = join(STORE_ROOT, "installation-started-sent");
|
|
3581
|
+
/**
|
|
3582
|
+
* Send `installation_started` if this installation has not had it accepted yet. Never rejects.
|
|
3583
|
+
*
|
|
3584
|
+
* Asks whether it may send before it reads anything, so under `PRB_TELEMETRY=off` it creates no
|
|
3585
|
+
* identity, no marker and no log line: the kill switch is not a decision, and a machine that
|
|
3586
|
+
* unsets it tomorrow has still never been counted.
|
|
3587
|
+
*/
|
|
3588
|
+
async function announceInstallation() {
|
|
3589
|
+
try {
|
|
3590
|
+
if (!telemetryUploadEnabled()) return;
|
|
3591
|
+
if (announced()) return;
|
|
3592
|
+
const line = stamp({ event: "installation_started" });
|
|
3593
|
+
if (!line || !await deliver(line)) return;
|
|
3594
|
+
writeMarker();
|
|
3595
|
+
append(line);
|
|
3596
|
+
} catch {}
|
|
3597
|
+
}
|
|
3598
|
+
/** An unreadable home counts as already sent: failing closed on repetition. */
|
|
3599
|
+
function announced() {
|
|
3600
|
+
try {
|
|
3601
|
+
return existsSync(MARKER);
|
|
3602
|
+
} catch {
|
|
3603
|
+
return true;
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
function writeMarker() {
|
|
3607
|
+
mkdirSync(STORE_ROOT, { recursive: true });
|
|
3608
|
+
writeFileSync(MARKER, `${(/* @__PURE__ */ new Date()).toISOString()}\n`, { mode: 384 });
|
|
3486
3609
|
}
|
|
3487
3610
|
//#endregion
|
|
3488
3611
|
//#region src/main.ts
|
|
@@ -3520,28 +3643,33 @@ function greet(command) {
|
|
|
3520
3643
|
/**
|
|
3521
3644
|
* The one-time telemetry disclosure, printed after the hello and before the work.
|
|
3522
3645
|
*
|
|
3523
|
-
*
|
|
3646
|
+
* On the commands `isProductCommand` names, which are also the ones that may send
|
|
3647
|
+
* `installation_started`, so nothing is transmitted from a command that could not have disclosed
|
|
3648
|
+
* it. Not `config`, which is where the setting is changed: somebody typing
|
|
3524
3649
|
* `config set telemetry-upload off` should be able to turn it off without being told first that it
|
|
3525
|
-
* is on.
|
|
3650
|
+
* is on.
|
|
3526
3651
|
*
|
|
3527
3652
|
* On stderr, like every other line here that is not a result, so `prreviewbuddy review | pbcopy`
|
|
3528
3653
|
* still copies a URL. Unlike the question it replaced it takes no input and checks for no terminal:
|
|
3529
3654
|
* there is nobody to wait for, and a headless install that transmits is the one that most needs
|
|
3530
3655
|
* telling.
|
|
3531
3656
|
*/
|
|
3532
|
-
var NOTIFIED = [
|
|
3533
|
-
"review",
|
|
3534
|
-
"open",
|
|
3535
|
-
"agents"
|
|
3536
|
-
];
|
|
3537
3657
|
function notify(command) {
|
|
3538
|
-
if (!
|
|
3658
|
+
if (!isProductCommand(command)) return;
|
|
3539
3659
|
noticeTelemetry((chunk) => void process.stderr.write(chunk));
|
|
3540
3660
|
}
|
|
3541
3661
|
async function main(argv) {
|
|
3542
3662
|
const command = parse(argv);
|
|
3543
3663
|
greet(command);
|
|
3544
3664
|
notify(command);
|
|
3665
|
+
const announced = isProductCommand(command) ? announceInstallation() : Promise.resolve();
|
|
3666
|
+
try {
|
|
3667
|
+
return await run(command);
|
|
3668
|
+
} finally {
|
|
3669
|
+
await announced;
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3672
|
+
async function run(command) {
|
|
3545
3673
|
switch (command.name) {
|
|
3546
3674
|
case "help":
|
|
3547
3675
|
out(HELP);
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as storedScopes, $n as loadWorkspace, A as reanalyseReview, An as endedAt, Ar as issuesOutstanding, At as renderMarkdown, Br as isCommitReview, Bt as createWorktree, C as track, Cn as remoteHost, Cr as queueAddMany, Ct as unreadCount$1, D as endRun, Dn as followedBranch, Dr as doneVerb, Dt as authorPreview, E as completeRun, En as checkReview, Er as readQueue, Et as UNESTABLISHED_MOTIVATION, F as record, Fr as describeAuthorship, Ft as asString, G as describeRuntimes, Gn as groupByLineage, Gr as readAgentPreference, Gt as requestJobStop, H as mapRuntimes, Hn as isClaimed, Ht as isWorktreeHeld, In as isLiveJob, Ir as WITHHELD_REASON, It as extractJsonText, J as loadArchitecture, Jn as kissDecisionRefusal, Jr as STORE_ROOT, Jt as activateQueued, K as ARCHITECTURE_DIR, Kn as indexKeptIds, Kr as writeAgentPreference, Ln as phasesFor, Lr as kissOutcome, Lt as objectWithAnyOf, Mn as isTerminal, Mr as reviewerDispositions, Mt as CODE_COPY_CLASS, Nn as loadJob, Nr as processDiscussion, Nt as escapeHtml$1, O as reportRun, On as reviewedShaOf, Or as isIssueOutstanding, Ot as hasAuthorContent, P as readEvents, Pn as saveJob, Pr as recommendationsOf, Pt as renderInlineText, Q as storeArchitecture, Qn as lineagePosition, Qr as agentFor, Qt as readIndexToken, R as recordTelemetryUploadConsent, Rn as progressSteps, Rt as ReviewBeingDeletedError, S as isDriving, Sn as forgeFor, Sr as queueAdd, St as toneOf, T as beginRun, Tn as checkCodeFreshness, Tr as queueStamp, Tt as EXPLAIN_SIMPLY_PROMPT, U as recordRuntimeBestEffort, Un as decideKissRecommendation, Ur as resolveModel, Ut as readMarker, V as waitingHistory, Vr as git, Vt as holdWorktree, W as RUNTIME_WORDS, Wn as followedRefName, Wr as clearAgentPreference, Wt as removeWorktree, X as readArchitecture, Xn as latestKissRun, Y as partFiles, Yn as kissDecisionsOf, Yr as AGENT_IDS, Yt as compactQueue, Z as repositoryKey, Zt as settleQueue, _ as liveUpdateFor, _n as resolveWorkspacePort, _r as lifecycleItemLabel, _t as markRead, a as wasBlocked, an as spawnServer, ar as recentWorkspaces, at as isArchitectureId, bn as claimRequestUrl, br as parseLifecycle, bt as readNotification, c as RefreshUnavailableError, ct as branchOf, d as mergeUpdateInto, dr as workspaceStamps, dt as createPrState, ei as AgentCancelledError, en as clearServerRecordOwnedBy, et as trackedFiles, f as updateReview, fn as feedbackUrl, fr as LIFECYCLES, ft as createPrUpdate, g as UpdateAlreadyRunningError, gn as parseWorkspacePort, gr as lifecycleChip, gt as markAllRead, h as askCheckout, hn as MIN_WORKSPACE_PORT, hr as isLifecycle, ht as listNotifications, i as liveJobsFor, in as serverEntryExists, ir as previousKissResult, it as ancestorsOf, j as runJob, jn as fail, jr as questionsOutstanding, jt as CODE_BLOCK_CLASS, k as stopRun, kn as allJobs, kr as isQuestionOutstanding, kt as GITHUB_MARKDOWN, l as cancelUpdate, ln as writeServerRecord, lr as touchWorkspace, lt as subjectOf, m as isUnchanged, mn as MAX_WORKSPACE_PORT, mr as activityLabel, mt as headline, n as deleteReview, nn as readServerRecord, nt as QUOTE_LIMITS, o as discardJob, on as stopChild, or as reviewedCommit, ot as writeJsonAtomically, p as refreshPrContext, pn as DEFAULT_WORKSPACE_PORT, pr as activityAttributes, pt as eventName, q as checkArchitecture, qn as indexReviewGroups, qt as relativeTime$1, r as lineageIds, rr as presentedKissRunOf, rt as REPOSITORY_SCOPE, s as FETCH_TIMEOUT_MS, st as announceJob, t as deleteLineage, ti as AgentUnavailableError, tr as mutateWorkspace, tt as ownersOf, u as cancellableHere, un as BUILD_VERSION, ur as workspaceRevision, ut as createArchitectureNotification, vn as statedWorkspacePort, vr as lifecyclePill, vt as markReadForReview, w as RUN_KINDS, wn as fillFileUrlTemplate, wr as queueRemove, wt as withUsageRecorded, x as startKissJob, xr as sectionOf, xt as removeNotification, y as agentEnvOf, yn as writePortPreference, yr as offersLifecycle, yt as pruneNotifications, z as telemetryUploadConsent, zn as purposeOf, zt as MANAGED_ROOT } from "./delete_review-CeJo9t7h.js";
|
|
2
2
|
import { basename, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prreviewbuddy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.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",
|