Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
b9ca174
docs(devlog): plan opt-in telemetry and the opencodex.me usage dashboard
lidge-jun Nov 7, 2026
0c542de
feat(telemetry): add shared wire contract, config schema, and core te…
lidge-jun Nov 7, 2026
643dd4f
feat(telemetry): add management API routes, CLI commands, and consent…
lidge-jun Nov 7, 2026
c47ff3d
test(telemetry): add client telemetry test suite and structure ownership
lidge-jun Nov 7, 2026
b5eddc3
fix(telemetry): remove state installId leak, harden writes, and prese…
lidge-jun Nov 7, 2026
3e4597d
docs(devlog): record PR review round 1 decisions for the telemetry stack
lidge-jun Nov 7, 2026
9e0284d
fix(telemetry): apply review round 1 client core, consent, and privac…
lidge-jun Nov 7, 2026
64ac26a
Merge layer 1 review-round plan into layer 2
lidge-jun Nov 7, 2026
8f0b86e
test(telemetry): prove env opt-out days of a long-running proxy are n…
lidge-jun Nov 8, 2026
a09bb51
fix(telemetry): keep a long-running env opt-out period ineligible unt…
lidge-jun Nov 8, 2026
b6c9314
docs(devlog): plan the env opt-out marker, privacy statement files, a…
lidge-jun Nov 8, 2026
88c5586
Merge layer 1 plan update into layer 2
lidge-jun Nov 8, 2026
11a0f8f
test(telemetry): pin that an unreachable or undeployed collector fail…
lidge-jun Nov 8, 2026
ed53c2e
test(telemetry): prove zero sends before noticeShownAt across ten und…
lidge-jun Nov 8, 2026
b4dd934
fix(telemetry): let ocx telemetry disable create config.json on a fre…
lidge-jun Nov 8, 2026
4ba7d13
feat(desktop): Windows installer page to disclose telemetry and opt o…
lidge-jun Nov 8, 2026
0e3c03a
docs(devlog): record the owner's SignPath decision (D21) and the Wind…
lidge-jun Nov 8, 2026
3fe3652
Merge layer 1 SignPath decision into layer 2
lidge-jun Nov 8, 2026
f8e78a2
Merge layer 2 into layer 2b
lidge-jun Nov 8, 2026
04abd39
fix(telemetry): hold a pre-config decision instead of creating config…
lidge-jun Nov 8, 2026
6d387ff
docs(telemetry): match the privacy statement and AGENTS_INSTALL to th…
lidge-jun Nov 8, 2026
b053cd5
Merge layer 2 round-3 fixes into layer 2b, and harden the installer o…
lidge-jun Nov 8, 2026
5f0e29c
fix(config): keep a telemetry decision on disk through a live setting…
lidge-jun Nov 8, 2026
5b88d16
fix(telemetry): keep a running opted-out proxy's days unsent, fail cl…
lidge-jun Nov 8, 2026
1fb26f9
Merge layer 2 round-3 fixes (live save, opt-out owners, state module)…
lidge-jun Nov 8, 2026
5ded863
fix(desktop): make the installer opt-out machine-wide, keep it throug…
lidge-jun Nov 8, 2026
7aee518
fix(config,telemetry): carry validated on-disk consent through every …
lidge-jun Nov 8, 2026
98af425
Merge layer 2 save-path hardening into layer 2b
lidge-jun Nov 8, 2026
bc5cbe8
fix(desktop): keep a carried installer choice's stamp, compare optout…
lidge-jun Nov 8, 2026
c9443b1
fix(desktop): issue a new installer stamp only when the page changes …
lidge-jun Nov 8, 2026
12d45a3
fix(telemetry): never overwrite an unreadable state file after a send…
lidge-jun Nov 8, 2026
c7215bc
Merge branch 'goat/telemetry-02-client-core' into goat/telemetry-02b-…
lidge-jun Nov 8, 2026
0a0ea74
docs(privacy): name the per-day install hash in the privacy statement
lidge-jun Nov 8, 2026
22391f9
Merge branch 'goat/telemetry-02-client-core' into goat/telemetry-02b-…
lidge-jun Nov 8, 2026
94e15a6
fix(desktop): keep the carried stamp after Back and Next, fail closed…
lidge-jun Nov 8, 2026
e8f8662
fix(desktop): schedule the installer's default choice after an action…
lidge-jun Nov 8, 2026
37d09e9
fix(config,telemetry): a save that creates config.json never introduc…
lidge-jun Nov 8, 2026
156301c
Merge branch 'goat/telemetry-02-client-core' into goat/telemetry-02b-…
lidge-jun Nov 8, 2026
cb3398e
fix(desktop): compare every installer condition explicitly, so a carr…
lidge-jun Nov 8, 2026
d6fc2e2
fix(desktop): allow Windows-only installer telemetry items to be unus…
lidge-jun Nov 8, 2026
3e5afbc
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 8, 2026
d10d528
ci: approve the skill-surface generator change in the release evidenc…
lidge-jun Nov 8, 2026
eec1047
fix(telemetry): read consent only from the telemetry block, and fail …
lidge-jun Nov 8, 2026
25633e9
fix(telemetry): keep a held decision out of a newly created config.json
lidge-jun Nov 8, 2026
f5a8ae0
fix(telemetry): fail closed on a malformed held decision
lidge-jun Nov 8, 2026
b2386d8
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 8, 2026
e367ca6
docs(structure): map the telemetry consent, carry and lifecycle files…
lidge-jun Nov 8, 2026
fcb44f5
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 8, 2026
594f32e
Merge remote-tracking branch 'origin/goat/telemetry-02-client-core' i…
lidge-jun Nov 8, 2026
7fd8b5e
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 8, 2026
c837c6a
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 8, 2026
e85008f
Merge remote-tracking branch 'origin/goat/telemetry-02-client-core' i…
lidge-jun Nov 8, 2026
e27855e
ci: retrigger on current dev
lidge-jun Nov 9, 2026
64f74cb
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 9, 2026
d2b4a5d
docs(skill): regenerate the surface counts after merging dev
lidge-jun Nov 9, 2026
a78e36b
Merge remote-tracking branch 'origin/goat/telemetry-02-client-core' i…
lidge-jun Nov 9, 2026
6ff1b87
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 9, 2026
06e1592
Merge remote-tracking branch 'origin/goat/telemetry-02-client-core' i…
lidge-jun Nov 9, 2026
ef03c55
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02-clie…
lidge-jun Nov 9, 2026
53898fd
Merge remote-tracking branch 'origin/goat/telemetry-02-client-core' i…
lidge-jun Nov 9, 2026
c8cc1d1
Merge remote-tracking branch 'origin/dev' into goat/telemetry-02b-msi…
lidge-jun Nov 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ Free code signing provided by [SignPath.io](https://about.signpath.io/), certifi

- Committers and reviewers: [lidge-jun](https://github.com/lidge-jun)
- Approvers: [lidge-jun](https://github.com/lidge-jun)
- Privacy: OpenCodex sends one anonymous daily usage summary (a per-day install hash that changes daily, version, release channel, platform, install channel, provider kind, coarse model family, active minutes and request, token and error counts; never prompts, outputs, keys, accounts, hosts or paths) to `telemetry.opencodex.me` only after the person installing or operating it has seen the notice (dashboard popup or interactive CLI prompt) and has not declined; it can be turned off in that notice and anytime with `ocx telemetry disable`, `OCX_TELEMETRY=0`, `DO_NOT_TRACK=1` or Proxy details; project-wide aggregates are public; otherwise data goes only where the user configures.
- Privacy: OpenCodex sends one anonymous daily usage summary (a per-day install hash that changes daily, version, release channel, platform, install channel, provider kind, coarse model family, active minutes and request, token and error counts; never prompts, outputs, keys, accounts, hosts or paths) to `telemetry.opencodex.me` only after the person installing or operating it has seen the notice (dashboard popup or interactive CLI prompt) and has not declined; it can be turned off in that notice, in the Windows installer (a "Send anonymous usage statistics" checkbox, checked by default) and anytime with `ocx telemetry disable`, `OCX_TELEMETRY=0`, `DO_NOT_TRACK=1` or Proxy details; project-wide aggregates are public; otherwise data goes only where the user configures.

## Disclaimer

Expand Down
188 changes: 188 additions & 0 deletions desktop/scripts/telemetry-installer-dialog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import {
TELEMETRY_DISCLOSURE_HEADING,
TELEMETRY_DISCLOSURE_POINTS,
TELEMETRY_POLICY_URL,
} from "../../src/telemetry/disclosure";

/**
* Generates the WiX fragment that adds the telemetry choice to the Windows installer.
*
* SignPath Foundation's conditions say software that sends usage data must describe it in a privacy
* policy, display that policy during installation, and include an installation option to disable
* it. The fragment inserts a dialog between the welcome and install-directory pages of Tauri's
* WixUI_InstallDir sequence. It shows the same disclosure the CLI prompt prints, links the policy,
* and offers a "Send anonymous usage statistics" checkbox, checked by default (unchecked when the
* install already carries an opt-out). The choice and a stamp of when it was made go to HKLM, so a
* silent install run as SYSTEM covers every user, and an upgrade carries the previous choice forward.
* The desktop app (`installer_telemetry.rs`) persists an opt-out into each user's opencodex config
* once per stamp, before it starts or attaches to a proxy.
*
* Silent installs choose with the public property: `msiexec /i OpenCodex.msi /qn OCX_TELEMETRY_CHOICE=optout`.
*
* `bun desktop/scripts/telemetry-installer-dialog.ts --write` regenerates the committed file;
* `--check` fails when it is stale.
*/

export const TELEMETRY_INSTALLER_FRAGMENT_PATH = "wix/telemetry-choice.wxs";
export const TELEMETRY_INSTALLER_COMPONENT_GROUP = "OcxTelemetryInstallerChoice";
export const TELEMETRY_INSTALLER_DIALOG = "OcxTelemetryDlg";
export const TELEMETRY_INSTALLER_REGISTRY_KEY = "Software\\OpenCodex\\Telemetry";
export const TELEMETRY_INSTALLER_REGISTRY_VALUE = "InstallerChoice";
export const TELEMETRY_INSTALLER_STAMP_VALUE = "InstallerChoiceStamp";
export const TELEMETRY_INSTALLER_STAMP_PROPERTY = "OCX_TELEMETRY_STAMP";
export const TELEMETRY_INSTALLER_CHOICE_PROPERTY = "OCX_TELEMETRY_CHOICE";
export const TELEMETRY_INSTALLER_CHECKBOX_PROPERTY = "OCX_TELEMETRY_SEND";
export const TELEMETRY_INSTALLER_CHECKBOX_TEXT = "Send anonymous usage statistics";
// Shown under the checkbox: "send" is never acted on, and a new opt-out applies to every account.
export const TELEMETRY_INSTALLER_KEEP_NOTE =
"Checked keeps each account's choice; unchecking turns it off for all.";
export const TELEMETRY_INSTALLER_SEND = "send";
export const TELEMETRY_INSTALLER_OPT_OUT = "optout";
// Fixed so upgrades keep one component identity for the HKLM values (new when the key path moved to HKLM).
const COMPONENT_GUID = "B3E1C7D2-58A4-4F0E-9C6B-7A2D41E95F08";

// Dialog units. WiX's standard dialogs are 370 x 270 with the button row below y = 234.
const TEXT_LEFT = 20;
const TEXT_WIDTH = 330;
const CHARS_PER_LINE = 72;
const LINE_HEIGHT = 10;
const POINTS_TOP = 50;
const POINT_GAP = 2;

function xmlAttr(value: string): string {
// MSI formats control text, so brackets would be read as property references.
if (/[[\]{}]/.test(value)) throw new Error(`Installer text must not contain brackets or braces: ${value}`);
// The en-US MSI database uses code page 1252; keep to ASCII plus the bullet it also carries.
if (!/^[\x20-\x7E•]*$/.test(value)) throw new Error(`Installer text must stay within code page 1252: ${value}`);
return value
.replaceAll("&", "&")
.replaceAll('"', """)
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;");
}

function wrappedLines(text: string): number {
let lines = 1;
let width = 0;
for (const word of text.split(" ")) {
const next = width === 0 ? word.length : width + 1 + word.length;
if (next > CHARS_PER_LINE && width > 0) {
lines += 1;
width = word.length;
} else {
width = next;
}
}
return lines;
}

export function renderTelemetryInstallerFragment(): string {
const controls: string[] = [];
let y = POINTS_TOP;
TELEMETRY_DISCLOSURE_POINTS.forEach((point, index) => {
const height = wrappedLines(point) * LINE_HEIGHT;
controls.push(
` <Control Id="Point${index + 1}" Type="Text" X="${TEXT_LEFT}" Y="${y}" Width="${TEXT_WIDTH}" Height="${height}" NoPrefix="yes" Text="${xmlAttr(`• ${point}`)}" />`,
);
y += height + POINT_GAP;
});
const linkY = y + 2;
const checkboxY = linkY + 14;
const noteY = checkboxY + 17;
if (noteY + LINE_HEIGHT > 232) {
throw new Error(`The telemetry disclosure no longer fits the installer dialog (it ends at ${noteY + LINE_HEIGHT}).`);
}
return `<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by desktop/scripts/telemetry-installer-dialog.ts from src/telemetry/disclosure.ts. Do not edit by hand. -->
<?if $(sys.BUILDARCH) = "x86" ?>
<?define OcxTelemetryWin64 = "no" ?>
<?else ?>
<?define OcxTelemetryWin64 = "yes" ?>
<?endif ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<!-- Public: a silent install opts out with ${TELEMETRY_INSTALLER_CHOICE_PROPERTY}=${TELEMETRY_INSTALLER_OPT_OUT}. -->
<Property Id="${TELEMETRY_INSTALLER_CHOICE_PROPERTY}" Secure="yes" />
<Property Id="${TELEMETRY_INSTALLER_STAMP_PROPERTY}" Secure="yes" />
<Property Id="${TELEMETRY_INSTALLER_CHECKBOX_PROPERTY}" Secure="yes" />
<!-- An upgrade removes the old product early, so read the previous choice first and carry it. -->
<Property Id="OCX_TELEMETRY_PREVIOUS_CHOICE">
<RegistrySearch Id="OcxTelemetryPreviousChoice" Root="HKLM" Key="${TELEMETRY_INSTALLER_REGISTRY_KEY}" Name="${TELEMETRY_INSTALLER_REGISTRY_VALUE}" Type="raw" Win64="$(var.OcxTelemetryWin64)" />
</Property>
<Property Id="OCX_TELEMETRY_PREVIOUS_STAMP">
<RegistrySearch Id="OcxTelemetryPreviousStamp" Root="HKLM" Key="${TELEMETRY_INSTALLER_REGISTRY_KEY}" Name="${TELEMETRY_INSTALLER_STAMP_VALUE}" Type="raw" Win64="$(var.OcxTelemetryWin64)" />
</Property>
<!-- Fail closed: any command-line value other than send (off, 0, a typo, padding) is an opt-out.
Every condition here compares with "" because a bare property holding the integer 0 is false. -->
<SetProperty Action="OcxTelemetryNormalizeChoice" Id="${TELEMETRY_INSTALLER_CHOICE_PROPERTY}" Value="${TELEMETRY_INSTALLER_OPT_OUT}" Before="OcxTelemetryStampExplicit" Sequence="both">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} &lt;&gt; "" AND ${TELEMETRY_INSTALLER_CHOICE_PROPERTY} ~&lt;&gt; "${TELEMETRY_INSTALLER_SEND}"</SetProperty>
<!-- A choice given on the command line is a new decision: give it a new stamp. The UI phase passes
its stamp to the install phase, so a carried choice is never re-stamped without the page. -->
<SetProperty Action="OcxTelemetryStampExplicit" Id="${TELEMETRY_INSTALLER_STAMP_PROPERTY}" Value="[Date] [Time]" Before="AppSearch" Sequence="both">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} &lt;&gt; "" AND ${TELEMETRY_INSTALLER_STAMP_PROPERTY} = ""</SetProperty>
<SetProperty Action="OcxTelemetryKeepChoice" Id="${TELEMETRY_INSTALLER_CHOICE_PROPERTY}" Value="[OCX_TELEMETRY_PREVIOUS_CHOICE]" After="AppSearch" Sequence="both">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} = "" AND OCX_TELEMETRY_PREVIOUS_CHOICE &lt;&gt; ""</SetProperty>
<SetProperty Action="OcxTelemetryKeepStamp" Id="${TELEMETRY_INSTALLER_STAMP_PROPERTY}" Value="[OCX_TELEMETRY_PREVIOUS_STAMP]" After="OcxTelemetryKeepChoice" Sequence="both">${TELEMETRY_INSTALLER_STAMP_PROPERTY} = "" AND OCX_TELEMETRY_PREVIOUS_STAMP &lt;&gt; ""</SetProperty>
<!-- The stamp the page started from, restored when Next leaves the carried choice unchanged. -->
<SetProperty Action="OcxTelemetryCarriedStamp" Id="OCX_TELEMETRY_CARRIED_STAMP" Value="[${TELEMETRY_INSTALLER_STAMP_PROPERTY}]" After="OcxTelemetryKeepStamp" Sequence="ui">${TELEMETRY_INSTALLER_STAMP_PROPERTY} &lt;&gt; ""</SetProperty>
<!-- After KeepStamp, which runs in both sequences: WiX's linker rejects a parent missing from a sequence. -->
<SetProperty Action="OcxTelemetryDefaultChoice" Id="${TELEMETRY_INSTALLER_CHOICE_PROPERTY}" Value="${TELEMETRY_INSTALLER_SEND}" After="OcxTelemetryKeepStamp" Sequence="both">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} = ""</SetProperty>
<!-- The box starts checked only when the choice is send, so a carried opt-out (or any other carried value) starts unchecked. -->
<SetProperty Action="OcxTelemetryCheckBox" Id="${TELEMETRY_INSTALLER_CHECKBOX_PROPERTY}" Value="1" After="OcxTelemetryDefaultChoice" Sequence="ui">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} ~= "${TELEMETRY_INSTALLER_SEND}"</SetProperty>
<ComponentGroup Id="${TELEMETRY_INSTALLER_COMPONENT_GROUP}">
<!-- Machine-wide, so an opt-out from a silent install run as SYSTEM reaches every user. -->
<Component Id="OcxTelemetryInstallerChoiceRegistry" Directory="INSTALLDIR" Guid="${COMPONENT_GUID}" Win64="$(var.OcxTelemetryWin64)">
<RegistryValue Root="HKLM" Key="${TELEMETRY_INSTALLER_REGISTRY_KEY}" Name="${TELEMETRY_INSTALLER_REGISTRY_VALUE}" Type="string" Value="[${TELEMETRY_INSTALLER_CHOICE_PROPERTY}]" KeyPath="yes" />
<RegistryValue Root="HKLM" Key="${TELEMETRY_INSTALLER_REGISTRY_KEY}" Name="${TELEMETRY_INSTALLER_STAMP_VALUE}" Type="string" Value="[${TELEMETRY_INSTALLER_STAMP_PROPERTY}]" />
</Component>
</ComponentGroup>
<UI>
<Dialog Id="${TELEMETRY_INSTALLER_DIALOG}" Width="370" Height="270" Title="!(loc.InstallDirDlg_Title)">
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="{\\WixUI_Font_Title}Usage statistics" />
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="${xmlAttr(TELEMETRY_DISCLOSURE_HEADING)}" />
${controls.join("\n")}
<Control Id="PolicyLink" Type="Hyperlink" X="${TEXT_LEFT}" Y="${linkY}" Width="${TEXT_WIDTH}" Height="12">
<Text><![CDATA[<a href="${TELEMETRY_POLICY_URL}">Read the full privacy policy</a>]]></Text>
</Control>
<Control Id="SendCheckBox" Type="CheckBox" X="${TEXT_LEFT}" Y="${checkboxY}" Width="${TEXT_WIDTH}" Height="17" Property="${TELEMETRY_INSTALLER_CHECKBOX_PROPERTY}" CheckBoxValue="1" Text="${TELEMETRY_INSTALLER_CHECKBOX_TEXT}" />
<Control Id="KeepNote" Type="Text" X="${TEXT_LEFT + 14}" Y="${noteY}" Width="${TEXT_WIDTH - 14}" Height="${LINE_HEIGHT}" NoPrefix="yes" Text="${xmlAttr(TELEMETRY_INSTALLER_KEEP_NOTE)}" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)">
<Publish Event="NewDialog" Value="WelcomeDlg">1</Publish>
</Control>
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<Publish Property="${TELEMETRY_INSTALLER_CHOICE_PROPERTY}" Value="${TELEMETRY_INSTALLER_SEND}">${TELEMETRY_INSTALLER_CHECKBOX_PROPERTY} = "1"</Publish>
<Publish Property="${TELEMETRY_INSTALLER_CHOICE_PROPERTY}" Value="${TELEMETRY_INSTALLER_OPT_OUT}">NOT (${TELEMETRY_INSTALLER_CHECKBOX_PROPERTY} = "1")</Publish>
<Publish Property="${TELEMETRY_INSTALLER_STAMP_PROPERTY}" Value="[Date] [Time]">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} ~&lt;&gt; OCX_TELEMETRY_PREVIOUS_CHOICE</Publish>
<Publish Property="${TELEMETRY_INSTALLER_STAMP_PROPERTY}" Value="[OCX_TELEMETRY_CARRIED_STAMP]">${TELEMETRY_INSTALLER_CHOICE_PROPERTY} ~= OCX_TELEMETRY_PREVIOUS_CHOICE AND OCX_TELEMETRY_CARRIED_STAMP &lt;&gt; ""</Publish>
<Publish Event="NewDialog" Value="InstallDirDlg">1</Publish>
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
</Dialog>
<!-- Tauri's template routes WelcomeDlg -> InstallDirDlg at Order 2; the later NewDialog wins. Only when
NOT Installed (a first install or a major upgrade): a patch of the installed product keeps Tauri's route. -->
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="${TELEMETRY_INSTALLER_DIALOG}" Order="3">NOT Installed</Publish>
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="${TELEMETRY_INSTALLER_DIALOG}" Order="3">NOT Installed</Publish>
</UI>
</Fragment>
</Wix>
`;
}

if (import.meta.main) {
const target = join(import.meta.dir, "..", "src-tauri", TELEMETRY_INSTALLER_FRAGMENT_PATH);
const expected = renderTelemetryInstallerFragment();
if (process.argv.includes("--write")) {
writeFileSync(target, expected, "utf8");
} else {
let actual = "";
try { actual = readFileSync(target, "utf8"); } catch { /* missing counts as stale */ }
if (actual !== expected) {
console.error(`${TELEMETRY_INSTALLER_FRAGMENT_PATH} is stale; run bun desktop/scripts/telemetry-installer-dialog.ts --write`);
process.exit(1);
}
}
}
Loading
Loading