Merge 919fdf4250c015a4f2ca589827bacb98e5d4083f into 1d10bcf54df8a53b04114256cd7ce04be30cf401
Diffinery authored 2 parents 1d10bcf919fdf4commit 2637c142699887599067fdbc267f53849970f0d6Browse files
1 file changed+35−44
modifiedapps/diffinery/src/web/agent-setup.tsx+35−44View file
| @@ -100,31 +100,29 @@ | ||
| 100 | 100 | |
| 101 | 101 | const TokenRow: FC<{ c: AppContext; base: string; t: AgentSetupToken }> = ({ base, t, c }) => { |
| 102 | 102 | const soon = Date.parse(t.expiresAt) - Date.now() < 14 * 86400_000; |
| 103 | + // A list row rather than a table row: on a phone the buttons wrap below the token's details. | |
| 103 | 104 | return ( |
| 104 | - <tr> | |
| 105 | - <td> | |
| 106 | - <b>{t.name}</b> | |
| 107 | - <div class="small muted mono">{t.prefix}…</div> | |
| 108 | - </td> | |
| 109 | - <td class={soon ? "warn" : ""}> | |
| 110 | - {date(t.expiresAt)} | |
| 111 | - <div class="small muted">created {date(t.createdAt)}</div> | |
| 112 | - </td> | |
| 113 | - <td class="small">{t.lastUsedAt ? <Time at={t.lastUsedAt} /> : <span class="muted">never</span>}</td> | |
| 114 | - <td style="text-align:right;white-space:nowrap"> | |
| 105 | + <li style="flex-wrap:wrap;align-items:center"> | |
| 106 | + <div class="grow" style="min-width:200px"> | |
| 107 | + <b>{t.name}</b> <span class="small muted mono">{t.prefix}…</span> | |
| 108 | + <div class="small muted"> | |
| 109 | + <span class={soon ? "warn" : ""}>Expires {date(t.expiresAt)}</span> · created {date(t.createdAt)} · {t.lastUsedAt ? <>last used <Time at={t.lastUsedAt} /></> : "never used"} | |
| 110 | + </div> | |
| 111 | + </div> | |
| 112 | + <div class="row" style="gap:6px"> | |
| 115 | 113 | <Form action={`${base}/tokens/${t.id}/renew`} t={csrfToken(c)} class="inline"> |
| 116 | 114 | <button class="btn sm" title={`Keep this token working for ${AGENT_SETUP_DAYS} days from today; nothing to paste again`}> |
| 117 | 115 | Renew |
| 118 | 116 | </button> |
| 119 | - </Form>{" "} | |
| 117 | + </Form> | |
| 120 | 118 | <Form action={`${base}/tokens/${t.id}/regenerate`} t={csrfToken(c)} class="inline" confirm="Revoke this token and make a new one? Sessions using it stop working until you paste the new one."> |
| 121 | 119 | <button class="btn sm">Revoke and regenerate</button> |
| 122 | - </Form>{" "} | |
| 120 | + </Form> | |
| 123 | 121 | <Form action={`${base}/tokens/${t.id}/revoke`} t={csrfToken(c)} class="inline" confirm="Revoke this token? Environments using it stop working."> |
| 124 | 122 | <button class="btn sm danger">Revoke</button> |
| 125 | 123 | </Form> |
| 126 | - </td> | |
| 127 | - </tr> | |
| 124 | + </div> | |
| 125 | + </li> | |
| 128 | 126 | ); |
| 129 | 127 | }; |
| 130 | 128 | |
| @@ -176,37 +174,30 @@ | ||
| 176 | 174 | </p> |
| 177 | 175 | )} |
| 178 | 176 | {tokens.length ? ( |
| 179 | - <table class="tbl mb"> | |
| 180 | - <thead> | |
| 181 | - <tr> | |
| 182 | - <th>Token</th> | |
| 183 | - <th>Expires</th> | |
| 184 | - <th>Last used</th> | |
| 185 | - <th /> | |
| 186 | - </tr> | |
| 187 | - </thead> | |
| 188 | - <tbody> | |
| 189 | - {tokens.map((k) => ( | |
| 190 | - <TokenRow c={c} base={base} t={k} /> | |
| 191 | - ))} | |
| 192 | - </tbody> | |
| 193 | - </table> | |
| 177 | + <ul class="list mb" style="border:1px solid var(--border2);border-radius:7px"> | |
| 178 | + {tokens.map((k) => ( | |
| 179 | + <TokenRow c={c} base={base} t={k} /> | |
| 180 | + ))} | |
| 181 | + </ul> | |
| 194 | 182 | ) : null} |
| 195 | - <Form action={`${base}/generate`} t={t} class="row" style="align-items:flex-end"> | |
| 196 | - <div class="field grow m0"> | |
| 197 | - <label for="tname">Label</label> | |
| 198 | - <input id="tname" name="name" type="text" maxlength={100} value={defaultTokenName()} /> | |
| 199 | - <div class="hint">Shown in the token list and in audit logs, so you can tell environments apart.</div> | |
| 183 | + {/* The button lines up with the input, and drops below it where both don't fit. */} | |
| 184 | + <Form action={`${base}/generate`} t={t} class="field m0"> | |
| 185 | + <label for="tname">Label</label> | |
| 186 | + <div class="row"> | |
| 187 | + <input id="tname" name="name" type="text" maxlength={100} value={defaultTokenName()} style="flex:1 1 240px;width:auto;min-width:0" /> | |
| 188 | + <button class="btn primary">{tokens.length ? "Generate another token" : "Generate agent token"}</button> | |
| 200 | 189 | </div> |
| 201 | - <button class="btn primary">{tokens.length ? "Generate another token" : "Generate agent token"}</button> | |
| 190 | + <div class="hint">Shown in the token list and in audit logs, so you can tell environments apart.</div> | |
| 202 | 191 | </Form> |
| 203 | 192 | {active ? ( |
| 204 | - <Form action={`${base}/merge`} t={t} class="row mt" style="align-items:center"> | |
| 205 | - <KCheckbox name="mayMerge" checked={agentMayMerge(active)} label="Can merge pull requests" /> | |
| 206 | - <button class="btn sm">Save</button> | |
| 207 | - <span class="hint" style="margin:0"> | |
| 193 | + <Form action={`${base}/merge`} t={t} class="mt"> | |
| 194 | + <div class="row"> | |
| 195 | + <KCheckbox name="mayMerge" checked={agentMayMerge(active)} label="Can merge pull requests" /> | |
| 196 | + <button class="btn sm">Save</button> | |
| 197 | + </div> | |
| 198 | + <p class="small muted" style="margin:6px 0 0"> | |
| 208 | 199 | It merges, or turns on auto-merge, only once a person approved the pull request's latest commit and every rule of the branch is met. A change to a workflow file always needs a person to merge. |
| 209 | - </span> | |
| 200 | + </p> | |
| 210 | 201 | </Form> |
| 211 | 202 | ) : null} |
| 212 | 203 | </div> |
| @@ -218,7 +209,7 @@ | ||
| 218 | 209 | </div> |
| 219 | 210 | <div class="body"> |
| 220 | 211 | <Paste value={host} /> |
| 221 | - <p class="small muted mt"> | |
| 212 | + <p class="small muted mt" style="margin-bottom:0"> | |
| 222 | 213 | The domain the environment must reach (the container registry is on it too). In Claude Code, set <b>Network access</b> to <b>Custom</b>, add it under <b>Allowed domains</b> and keep <b>Also include default list of common package managers</b> ticked; or choose <b>Full</b>. The default <b>Trusted</b> level |
| 223 | 214 | doesn't include it. |
| 224 | 215 | </p> |
| @@ -231,7 +222,7 @@ | ||
| 231 | 222 | </div> |
| 232 | 223 | <div class="body"> |
| 233 | 224 | <Paste value={script} /> |
| 234 | - <p class="small muted mt"> | |
| 225 | + <p class="small muted mt" style="margin-bottom:0"> | |
| 235 | 226 | Installs the <code>diffinery</code> CLI on PATH, makes git and gh use the token from the environment (it never writes the token to disk or prints it), clones {slug} unless it's there (into <code>/home/user/{repo.name}</code> in Claude Code; set <code>DIFFINERY_NO_CLONE=1</code> to skip), then checks that |
| 236 | 227 | everything works and reports to the Check below. Safe to run every session, and quick once the CLI is installed. <a href={`/${slug}/agent-setup.sh`}>Read the script</a>. |
| 237 | 228 | </p> |
| @@ -244,7 +235,7 @@ | ||
| 244 | 235 | </div> |
| 245 | 236 | <div class="body"> |
| 246 | 237 | <Paste value={env} copy={!!created} id="agent-env" /> |
| 247 | - <p class="small muted mt"> | |
| 238 | + <p class="small muted mt" style="margin-bottom:0"> | |
| 248 | 239 | {created |
| 249 | 240 | ? "Paste these lines as they are. The token is in two of them: the CLI and git read DIFFINERY_TOKEN, gh reads GH_ENTERPRISE_TOKEN, and GH_HOST points gh at Diffinery." |
| 250 | 241 | : tokens.length |