Offer the fill command in the page, ready to copy
A second row under Auto-fill, shown under the same condition -- an upload key
is present -- since a command that contributes is no use to someone who
cannot. The tarball URL is derived from the page's own location, so a preview
deployment hands out its own command rather than main's.
The key travels in the environment rather than in --key: argv is visible to
every user on the machine through `ps`, while another process's environment is
not. It is masked in what is shown and real in what is copied, so pasting onto
a fresh machine is one step while a screenshot of a page that has a key gives
nothing away -- which is what the password field is for. The button says so.
Where there is no clipboard to write to (an insecure origin) the full command
is revealed instead, copying having been the evident intent.
3 changed files+64−2
README.mdmodified+4−1View file
@@ -140,7 +140,10 @@ Nothing is published to the npm registry — npm installs a tarball from a URL
140140 as happily as from a package name, and the tarball is built and deployed
141141 beside the page, so the command line is always the same commit as the app.
142142 The page itself offers this command, ready to copy, once an upload key is
143-entered. The key can also be saved for later runs (`login` prompts for it and
143+entered; the key is masked in what the page shows and real in what it copies,
144+so that pasting it onto a fresh machine takes one step while a screenshot of
145+the page still gives nothing away. The key can also be saved for later runs
146+(`login` prompts for it and
144147 writes `~/.config/turing-surface-cache/key`), or passed as `--key`, though the
145148 environment is preferable: a key on the command line is visible to every user
146149 on the machine through `ps`, while another process's environment is not.
index.htmlmodified+18−0View file
@@ -85,6 +85,17 @@
8585 padding: 8px 12px; font-size: 13px; color: var(--ink-2);
8686 }
8787 .cloud .controls { padding: 2px 0 0; }
88+ /* The same walk, spelled as a command for a machine with no browser on
89+ it. The key is masked here and real in the clipboard, so the command
90+ is paste-and-go without the key ending up in a screenshot. */
91+ #clicmd {
92+ font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
93+ color: var(--ink); background: var(--sphere-bg);
94+ border: 1px solid var(--line); border-radius: 6px;
95+ padding: 3px 8px; max-width: 100%;
96+ overflow-x: auto; white-space: nowrap;
97+ }
98+ #clicopied { color: var(--ok); }
8899 #err { color: #b35900; white-space: pre-wrap; font-size: 13px; }
89100 </style>
90101 </head>
@@ -148,6 +159,13 @@
148159 title="Work through the parameter space on this machine, nearest the defaults first, computing and uploading whatever is not cached yet. Runs until stopped.">Auto-fill the cache</button>
149160 <span id="autonote"></span>
150161 </div>
162+ <div class="controls" id="clibar" hidden>
163+ <span>or on a machine with no browser on it:</span>
164+ <code id="clicmd"></code>
165+ <button id="clicopy"
166+ title="Copies the command with your key in it. The key is masked here so that it stays out of screenshots.">Copy command (includes your key)</button>
167+ <span id="clicopied"></span>
168+ </div>
151169 </div>
152170 <p id="err"></p>
153171 </main>
src/main.tsmodified+42−1View file
@@ -82,6 +82,10 @@ const elUploadNote = $('uploadnote');
8282 const elAutoBar = $('autobar');
8383 const elAuto = $<HTMLButtonElement>('auto');
8484 const elAutoNote = $('autonote');
85+const elCliBar = $('clibar');
86+const elCliCmd = $('clicmd');
87+const elCliCopy = $<HTMLButtonElement>('clicopy');
88+const elCliCopied = $('clicopied');
8589 const elErr = $('err');
8690
8791 /**
@@ -1031,11 +1035,48 @@ function updateUploadNote(): void {
10311035 elUploadNote.textContent = hasKey
10321036 ? 'uploads enabled — locally computed solutions will be contributed'
10331037 : '';
1034- // Auto-fill exists to contribute, so it is offered only to those who can.
1038+ // Auto-fill exists to contribute, so it is offered only to those who can,
1039+ // and so is the command that does the same thing elsewhere.
10351040 elAutoBar.hidden = !hasKey;
1041+ elCliBar.hidden = !hasKey;
1042+ elCliCmd.textContent = fillCommand('…');
1043+ elCliCopied.textContent = '';
10361044 if (!hasKey && autoRunning) autoRunning = false;
10371045 }
10381046
1047+/**
1048+ * The command that runs this same walk outside a browser. The tarball is
1049+ * deployed beside the page, so the URL is derived from this one and a preview
1050+ * deployment hands out its own command rather than main's.
1051+ *
1052+ * The key travels in the environment rather than in an option because argv is
1053+ * visible to every user on the machine through `ps`, while another process's
1054+ * environment is not. It is masked on screen and real in the clipboard: the
1055+ * displayed command would otherwise put the key in any screenshot of a page
1056+ * that has one, which is what the password field exists to prevent.
1057+ */
1058+function fillCommand(key: string): string {
1059+ const url = new URL('fill.tgz', location.href).href;
1060+ return `TURING_SURFACE_CACHE_KEY=${key} npx ${url}`;
1061+}
1062+
1063+elCliCopy.addEventListener('click', () => {
1064+ const key = elApiKey.value.trim();
1065+ if (!key) return;
1066+ navigator.clipboard.writeText(fillCommand(key)).then(
1067+ () => {
1068+ elCliCopied.textContent = 'copied';
1069+ setTimeout(() => (elCliCopied.textContent = ''), 4000);
1070+ },
1071+ () => {
1072+ // No clipboard (an insecure origin, usually). Copying was the intent, so
1073+ // show the whole thing and let it be selected by hand.
1074+ elCliCmd.textContent = fillCommand(key);
1075+ elCliCopied.textContent = 'clipboard unavailable — the key is now shown above';
1076+ },
1077+ );
1078+});
1079+
10391080 async function boot(): Promise<void> {
10401081 buildControls();
10411082 // Written even before any change, so the address bar is always shareable.