Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 1 | /** |
| 2 | * Pieces of the Actions pages: how a run, job or step stands, how long it | |
| 3 | * took, and a job's log as GitHub shows one, with its groups folded and | |
| 4 | * its errors and warnings marked. | |
| 5 | */ | |
| Merge branch 'worktree-agent-a3abfcce648e87dca' | 6 | import { AlertTriangle, Ban, Check, ChevronRight, CircleDashed, CircleSlash, Clock, Hourglass, Info, Loader2, ShieldAlert, X } from "lucide-react"; |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 7 | import { Fragment, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 8 | import { Link } from "react-router"; |
| 9 | ||
| 10 | import type { CommitStatus, Conclusion, LogChunk, WorkflowNote } from "@g1t/contracts"; | |
| 11 | ||
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 12 | import { Card } from "./ui/card"; |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 13 | import { Hint } from "./ui/hint"; |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 14 | import { markSpans, parseAnsi, spanLook } from "../lib/ansi"; |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 15 | import { type Line, type Row, type StepLog, appendLog, emptyLog, groupOf, logRows, rowKey, searchStepLog } from "../lib/log-lines"; |
| 16 | import { WINDOW_FROM, rowStarts, windowOf } from "../lib/log-window"; | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 17 | import { type PollAnswer, nextPoll, retryAfterMs } from "../lib/log-poll"; |
| Merge branch 'worktree-agent-ae1299e92e4462012' | 18 | |
| Merge branch 'worktree-agent-a3abfcce648e87dca' | 19 | /** |
| 20 | * Where a run, job or step stands. `of` says which: a run's `pending` waits | |
| 21 | * for its concurrency group and its `waiting` for its environments' rules, | |
| 22 | * while a job's `pending` is held at its environment and its `waiting` | |
| 23 | * waits for the jobs it needs. `environment` is the job's. | |
| 24 | */ | |
| 25 | type Standing = { status: string; conclusion: Conclusion | null; of?: "run" | "job"; environment?: string | null }; | |
| 26 | ||
| 27 | /** Held until a person (or a wait timer) lets it through. */ | |
| 28 | function heldWord({ status, of = "run", environment }: Standing): string | null { | |
| 29 | if (of === "run" && status === "action_required") return "Approval required"; | |
| 30 | if (of === "run" && status === "waiting") return "Waiting for review"; | |
| 31 | if (of === "job" && status === "pending") return environment ? `Waiting to deploy to ${environment}` : "Waiting at its environment"; | |
| 32 | return null; | |
| 33 | } | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 34 | |
| 35 | /** One icon for where a run, job or step stands. */ | |
| Merge branch 'worktree-agent-a3abfcce648e87dca' | 36 | export function StatusIcon({ status, conclusion, of = "run", environment, size = 16 }: Standing & { size?: number }) { |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 37 | if (status === "in_progress") return <Loader2 size={size} className="shrink-0 animate-spin text-warn" aria-label="Running" />; |
| Merge branch 'worktree-agent-a3abfcce648e87dca' | 38 | const held = heldWord({ status, conclusion, of, environment }); |
| 39 | if (held) | |
| 40 | return status === "action_required" ? ( | |
| 41 | <ShieldAlert size={size} className="shrink-0 text-warn" aria-label={held} /> | |
| 42 | ) : ( | |
| 43 | <Hourglass size={size} className="shrink-0 text-warn" aria-label={held} /> | |
| 44 | ); | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 45 | if (status !== "completed") |
| 46 | return <Clock size={size} className="shrink-0 text-faint" aria-label={status === "pending" ? "Waiting its turn" : "Queued"} />; | |
| 47 | switch (conclusion) { | |
| 48 | case "success": | |
| 49 | return ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 50 | <span className="inline-flex shrink-0 rounded-full bg-success/15 p-0.5 text-success" aria-label="Succeeded"> |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 51 | <Check size={size - 4} strokeWidth={3} /> |
| 52 | </span> | |
| 53 | ); | |
| 54 | case "failure": | |
| 55 | return ( | |
| 56 | <span className="inline-flex shrink-0 rounded-full bg-danger/15 p-0.5 text-danger" aria-label="Failed"> | |
| 57 | <X size={size - 4} strokeWidth={3} /> | |
| 58 | </span> | |
| 59 | ); | |
| 60 | case "cancelled": | |
| 61 | return <Ban size={size} className="shrink-0 text-faint" aria-label="Cancelled" />; | |
| 62 | default: | |
| 63 | return <CircleDashed size={size} className="shrink-0 text-faint" aria-label="Skipped" />; | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| Merge branch 'worktree-agent-a3abfcce648e87dca' | 67 | export function standingWord(standing: Standing): string { |
| 68 | const { status, conclusion } = standing; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 69 | if (status === "in_progress") return "Running"; |
| Merge branch 'worktree-agent-a3abfcce648e87dca' | 70 | const held = heldWord(standing); |
| 71 | if (held) return held; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 72 | if (status === "pending") return "Waiting for its concurrency group"; |
| 73 | if (status === "waiting") return "Waiting for the jobs it needs"; | |
| 74 | if (status === "calling") return "Running the workflow it calls"; | |
| 75 | if (status !== "completed") return "Queued"; | |
| 76 | return { success: "Succeeded", failure: "Failed", cancelled: "Cancelled", skipped: "Skipped" }[conclusion ?? "skipped"]; | |
| 77 | } | |
| 78 | ||
| 79 | /** `1m 12s`, from two times, or from a start until now. */ | |
| 80 | export function duration(start: string | null, end: string | null): string { | |
| 81 | if (!start) return ""; | |
| 82 | const ms = (end ? new Date(end).getTime() : Date.now()) - new Date(start).getTime(); | |
| 83 | const seconds = Math.max(0, Math.round(ms / 1000)); | |
| 84 | if (seconds < 60) return `${seconds}s`; | |
| 85 | const minutes = Math.floor(seconds / 60); | |
| 86 | if (minutes < 60) return `${minutes}m ${seconds % 60}s`; | |
| 87 | return `${Math.floor(minutes / 60)}h ${minutes % 60}m`; | |
| 88 | } | |
| 89 | ||
| Merge remote-tracking branch 'origin/main' into workspace-chat | 90 | /** |
| 91 | * How long something ran, as text. While it is still running the server and | |
| 92 | * the browser count to a different now, so the text may differ on hydration | |
| 93 | * (as TimeAgo's does). | |
| 94 | */ | |
| 95 | export function Duration({ start, end, className }: { start: string | null; end: string | null; className?: string }) { | |
| 96 | return ( | |
| 97 | <span className={className} suppressHydrationWarning> | |
| 98 | {duration(start, end)} | |
| 99 | </span> | |
| 100 | ); | |
| 101 | } | |
| 102 | ||
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 103 | /** `main` from `refs/heads/main`, `v1.2` from a tag, `#12` for a pull request. */ |
| 104 | export function shortRef(ref: string): string { | |
| 105 | const pull = /^refs\/pull\/(\d+)\//.exec(ref); | |
| 106 | if (pull) return `#${pull[1]}`; | |
| 107 | return ref.replace(/^refs\/(heads|tags)\//, ""); | |
| 108 | } | |
| 109 | ||
| 110 | const LINE_STYLE: Record<Line["kind"], string> = { | |
| 111 | text: "text-fg/85", | |
| 112 | error: "text-danger", | |
| 113 | warning: "text-warn", | |
| 114 | notice: "text-accent", | |
| 115 | debug: "text-faint", | |
| 116 | command: "text-muted", | |
| 117 | }; | |
| 118 | ||
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 119 | /** A line's time as the log shows it beside the line: GitHub's, to the millisecond. */ |
| 120 | function shownTime(at: string): string { | |
| 121 | return at.replace(/(\.\d{3})\d*Z$/, "$1Z"); | |
| 122 | } | |
| 123 | ||
| 124 | /** A line's text, coloured as the tool that printed it coloured it, and with a search's matches marked. */ | |
| 125 | function LineText({ text, ansi, query }: { text: string; ansi?: string; query: string }) { | |
| 126 | const spans = useMemo(() => (ansi ? parseAnsi(ansi) : [{ text, style: {} }]), [text, ansi]); | |
| 127 | const pieces = useMemo(() => markSpans(spans, query), [spans, query]); | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 128 | return ( |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 129 | <> |
| 130 | {pieces.map((piece, index) => { | |
| 131 | const look = spanLook(piece.style); | |
| 132 | const drawn = look.className || look.style ? ( | |
| 133 | <span className={look.className} style={look.style}> | |
| 134 | {piece.text} | |
| 135 | </span> | |
| 136 | ) : ( | |
| 137 | piece.text | |
| 138 | ); | |
| 139 | return piece.match ? ( | |
| 140 | <mark key={index} className="rounded-sm bg-accent/30 text-fg"> | |
| 141 | {drawn} | |
| 142 | </mark> | |
| 143 | ) : ( | |
| 144 | <Fragment key={index}>{drawn}</Fragment> | |
| 145 | ); | |
| 146 | })} | |
| 147 | </> | |
| 148 | ); | |
| 149 | } | |
| 150 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 151 | /** One line of a step's log: its number (a link to it), its time when asked for, and its text. Drawn again only when it changes. */ |
| 152 | type LogLineProps = { line: Line; number: number; query?: string; timestamps?: boolean; anchor?: string; targeted?: boolean }; | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 153 | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 154 | const LogLine = memo(function LogLine({ line, number, query = "", timestamps = false, anchor, targeted = false }: LogLineProps) { |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 155 | const label = /^(Error|Warning|Notice): /; |
| 156 | const text = line.text.replace(label, ""); | |
| 157 | const ansi = line.ansi?.replace(label, ""); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 158 | const id = anchor ? `${anchor}:${number}` : undefined; |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 159 | return ( |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 160 | <div |
| 161 | id={id} | |
| 162 | className={`flex scroll-mt-24 gap-4 px-4 hover:bg-raised/40 ${targeted ? "bg-accent/15" : line.kind === "error" ? "bg-danger/5" : ""}`} | |
| 163 | > | |
| 164 | {id ? ( | |
| 165 | <a href={`#${id}`} className="w-8 shrink-0 select-none text-right text-faint/70 hover:text-fg hover:underline"> | |
| 166 | {number} | |
| 167 | </a> | |
| 168 | ) : ( | |
| 169 | <span className="w-8 shrink-0 select-none text-right text-faint/70">{number}</span> | |
| 170 | )} | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 171 | {timestamps && ( |
| 172 | <time dateTime={line.at} className="w-[24ch] shrink-0 select-none text-faint/70 tabular-nums"> | |
| 173 | {line.at ? shownTime(line.at) : ""} | |
| 174 | </time> | |
| 175 | )} | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 176 | <span className={`min-w-0 whitespace-pre-wrap break-all ${LINE_STYLE[line.kind]}`}> |
| 177 | {line.kind === "error" && <span className="font-semibold">Error: </span>} | |
| 178 | {line.kind === "warning" && <span className="font-semibold">Warning: </span>} | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 179 | <LineText text={text} ansi={ansi} query={query} /> |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 180 | </span> |
| 181 | </div> | |
| 182 | ); | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 183 | }, sameLine); |
| 184 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 185 | /** Whether a line would draw the same: a log that grows keeps its lines, but a search finds them again. */ |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 186 | function sameLine(a: LogLineProps, b: LogLineProps): boolean { |
| 187 | return ( | |
| 188 | a.number === b.number && | |
| 189 | a.query === b.query && | |
| 190 | a.timestamps === b.timestamps && | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 191 | a.anchor === b.anchor && |
| 192 | a.targeted === b.targeted && | |
| 193 | a.line === b.line | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 194 | ); |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 195 | } |
| 196 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 197 | /** A group's title: a row that folds and opens the lines under it. */ |
| 198 | function GroupRow({ row, timestamps, onToggle }: { row: Extract<Row, { kind: "group" }>; timestamps: boolean; onToggle: (block: number) => void }) { | |
| 199 | return ( | |
| 200 | <button | |
| 201 | type="button" | |
| 202 | aria-expanded={row.open} | |
| 203 | onClick={() => onToggle(row.block)} | |
| 204 | className="flex w-full cursor-pointer items-center gap-1.5 px-4 text-left text-fg/85 hover:bg-raised/40" | |
| 205 | > | |
| 206 | <span className="w-8 shrink-0" /> | |
| 207 | {timestamps && ( | |
| 208 | <time dateTime={row.at} className="ml-2.5 w-[24ch] shrink-0 select-none text-faint/70 tabular-nums"> | |
| 209 | {row.at ? shownTime(row.at) : ""} | |
| 210 | </time> | |
| 211 | )} | |
| 212 | <ChevronRight size={12} className={`shrink-0 text-faint transition-transform ${row.open ? "rotate-90" : ""}`} /> | |
| 213 | <span className="min-w-0 whitespace-pre-wrap break-all">{row.title}</span> | |
| 214 | </button> | |
| 215 | ); | |
| 216 | } | |
| 217 | ||
| Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009) | 218 | /** |
| 219 | * A step's log. With `query`, only the lines holding it (any case), groups | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 220 | * opened, each match marked. With `timestamps`, each line's time beside it. |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 221 | * `anchor` names the step in each line's link (`step:3` gives `#step:3:12`), |
| 222 | * and `target` is the line such a link names: its group opens, and it is | |
| 223 | * scrolled to and marked. A log of more than a few thousand rows draws only | |
| 224 | * those on screen (lib/log-window.ts). | |
| Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009) | 225 | */ |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 226 | export function LogText({ |
| 227 | log, | |
| 228 | query = "", | |
| 229 | timestamps = false, | |
| 230 | anchor, | |
| 231 | target = null, | |
| 232 | }: { | |
| 233 | log: StepLog; | |
| 234 | query?: string; | |
| 235 | timestamps?: boolean; | |
| 236 | anchor?: string; | |
| 237 | target?: number | null; | |
| 238 | }) { | |
| 239 | const [open, setOpen] = useState<ReadonlySet<number>>(() => new Set()); | |
| 240 | const searching = query.trim().length > 0; | |
| 241 | const found = useMemo(() => searchStepLog(log, query), [log, query]); | |
| 242 | const rows = useMemo<Row[]>(() => (searching ? found.map((entry) => ({ kind: "line", entry })) : logRows(log, open)), [searching, found, log, open]); | |
| 243 | // A link to a line in a folded group opens the group. | |
| 244 | const targetGroup = target === null ? null : groupOf(log, target); | |
| 245 | useEffect(() => { | |
| 246 | if (targetGroup !== null) setOpen((known) => (known.has(targetGroup) ? known : new Set(known).add(targetGroup))); | |
| 247 | }, [targetGroup]); | |
| 248 | const toggle = useCallback((block: number) => { | |
| 249 | setOpen((known) => { | |
| 250 | const next = new Set(known); | |
| 251 | if (!next.delete(block)) next.add(block); | |
| 252 | return next; | |
| 253 | }); | |
| 254 | }, []); | |
| 255 | if (!log.hasText && !log.tail) return <p className="px-4 py-2 text-xs text-faint">No output.</p>; | |
| 256 | return <LogRows rows={rows} query={searching ? query : ""} timestamps={timestamps} anchor={anchor} target={target} onToggle={toggle} />; | |
| 257 | } | |
| 258 | ||
| 259 | /** The rows of a step's log, every one, or past `WINDOW_FROM` those on screen and a few around them. */ | |
| 260 | function LogRows({ | |
| 261 | rows, | |
| 262 | query, | |
| 263 | timestamps, | |
| 264 | anchor, | |
| 265 | target, | |
| 266 | onToggle, | |
| 267 | }: { | |
| 268 | rows: Row[]; | |
| 269 | query: string; | |
| 270 | timestamps: boolean; | |
| 271 | anchor?: string; | |
| 272 | target: number | null; | |
| 273 | onToggle: (block: number) => void; | |
| 274 | }) { | |
| 275 | const windowed = rows.length > WINDOW_FROM; | |
| 276 | const box = useRef<HTMLDivElement>(null); | |
| 277 | /** Each drawn row's height, by its key, as it was last measured. */ | |
| 278 | const heights = useRef(new Map<string, number>()); | |
| 279 | const [measured, setMeasured] = useState(0); | |
| 280 | /** The part of the log on screen, from its top, in steps of `VIEW_STEP` pixels. */ | |
| 281 | const [view, setView] = useState<[number, number]>([0, 0]); | |
| 282 | const starts = useMemo( | |
| 283 | () => (windowed ? rowStarts(rows.length, (index) => heights.current.get(rowKey(rows[index]!))) : null), | |
| 284 | // `measured` changes when a row's height does. | |
| 285 | // eslint-disable-next-line react-hooks/exhaustive-deps | |
| 286 | [windowed, rows, measured], | |
| 287 | ); | |
| 288 | const [from, to] = starts ? windowOf(starts, view[0], view[1]) : [0, rows.length]; | |
| 289 | // Follow the screen: the view, as the window scrolls or is resized. | |
| 290 | useEffect(() => { | |
| 291 | if (!windowed) return; | |
| 292 | let frame = 0; | |
| 293 | const look = () => { | |
| 294 | frame = 0; | |
| 295 | const rect = box.current?.getBoundingClientRect(); | |
| 296 | if (!rect) return; | |
| 297 | const top = Math.floor(-rect.top / VIEW_STEP) * VIEW_STEP; | |
| 298 | const bottom = Math.ceil((window.innerHeight - rect.top) / VIEW_STEP) * VIEW_STEP; | |
| 299 | setView((known) => (known[0] === top && known[1] === bottom ? known : [top, bottom])); | |
| 300 | }; | |
| 301 | const soon = () => { | |
| 302 | if (!frame) frame = requestAnimationFrame(look); | |
| 303 | }; | |
| 304 | look(); | |
| 305 | window.addEventListener("scroll", soon, { passive: true }); | |
| 306 | window.addEventListener("resize", soon); | |
| 307 | // A step folded or opened moves the log without a scroll. | |
| 308 | document.addEventListener("toggle", soon, true); | |
| 309 | return () => { | |
| 310 | if (frame) cancelAnimationFrame(frame); | |
| 311 | window.removeEventListener("scroll", soon); | |
| 312 | window.removeEventListener("resize", soon); | |
| 313 | document.removeEventListener("toggle", soon, true); | |
| 314 | }; | |
| 315 | }, [windowed]); | |
| 316 | // Measure what was drawn; a row whose height is not what was kept for it | |
| 317 | // has its space corrected. | |
| 318 | useLayoutEffect(() => { | |
| 319 | if (!windowed || !box.current) return; | |
| 320 | let changed = false; | |
| 321 | for (const element of box.current.querySelectorAll<HTMLElement>("[data-row]")) { | |
| 322 | const height = element.offsetHeight; | |
| 323 | const key = element.dataset.row!; | |
| 324 | if (height > 0 && heights.current.get(key) !== height) { | |
| 325 | heights.current.set(key, height); | |
| 326 | changed = true; | |
| 327 | } | |
| 328 | } | |
| 329 | if (changed) setMeasured((n) => n + 1); | |
| 330 | }); | |
| 331 | // A link to a line: scroll to it once it is in the log. | |
| 332 | const targetRow = target === null ? -1 : rows.findIndex((row) => row.kind === "line" && row.entry.number === target); | |
| 333 | const scrolledTo = useRef<number | null>(null); | |
| 334 | useLayoutEffect(() => { | |
| 335 | if (targetRow === -1 || scrolledTo.current === target || !box.current) return; | |
| 336 | scrolledTo.current = target; | |
| 337 | const top = box.current.getBoundingClientRect().top + window.scrollY + (starts ? starts[targetRow]! : 0); | |
| 338 | if (starts) window.scrollTo({ top: Math.max(0, top - window.innerHeight / 3) }); | |
| 339 | else document.getElementById(`${anchor}:${target}`)?.scrollIntoView({ block: "center" }); | |
| 340 | }, [targetRow, target, starts, anchor]); | |
| 341 | const drawn = rows.slice(from, to).map((row) => { | |
| 342 | const key = rowKey(row); | |
| Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009) | 343 | return ( |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 344 | <div key={key} data-row={windowed ? key : undefined}> |
| 345 | {row.kind === "line" ? ( | |
| 346 | <LogLine | |
| 347 | line={row.entry.line} | |
| 348 | number={row.entry.number} | |
| 349 | query={query} | |
| 350 | timestamps={timestamps} | |
| 351 | anchor={anchor} | |
| 352 | targeted={row.entry.number === target} | |
| 353 | /> | |
| 354 | ) : ( | |
| 355 | <GroupRow row={row} timestamps={timestamps} onToggle={onToggle} /> | |
| 356 | )} | |
| Merge Actions runs: summaries, attempts and re-runs, graceful cancel, log downloads, badges (actions 0009) | 357 | </div> |
| 358 | ); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 359 | }); |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 360 | return ( |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 361 | <div ref={box} className="py-1 font-mono text-xs leading-5"> |
| 362 | {starts && <div aria-hidden style={{ height: starts[from] }} />} | |
| 363 | {drawn} | |
| 364 | {starts && <div aria-hidden style={{ height: starts[rows.length]! - starts[to]! }} />} | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 365 | </div> |
| 366 | ); | |
| 367 | } | |
| 368 | ||
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 369 | /** How finely the view of a windowed log is followed, in pixels: a scroll of less draws nothing new. */ |
| 370 | const VIEW_STEP = 200; | |
| 371 | ||
| 372 | /** A job's log as the page has it: each step's log, read as it came (0 is the job's setup), when each last grew, and whether asking for more is failing. */ | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 373 | export type JobLog = { |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 374 | byStep: Map<number, StepLog>; |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 375 | /** When a chunk of each step's last arrived, by this browser's clock. */ |
| 376 | grewAt: Map<number, number>; | |
| 377 | /** The last ask failed, and the page is waiting to ask again. */ | |
| 378 | retrying: boolean; | |
| 379 | }; | |
| 380 | ||
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 381 | /** |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 382 | * A job's log, fetched as it grows (lib/log-poll.ts says how often), split |
| 383 | * by step. A failed fetch is tried again, waiting longer each time; while | |
| 384 | * the tab is hidden it waits, and asks at once when it is shown again. | |
| 385 | * `running` is whether one of the job's steps is running now. | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 386 | */ |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 387 | export function useJobLog(url: string, live: boolean, running: boolean): JobLog { |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 388 | // Each chunk is read once, into its step's log, as it arrives: a long |
| 389 | // step's log is not read again from its start for every chunk. | |
| 390 | const steps = useRef(new Map<number, StepLog>()); | |
| 391 | const [byStep, setByStep] = useState<Map<number, StepLog>>(new Map()); | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 392 | const [grewAt, setGrewAt] = useState<Map<number, number>>(new Map()); |
| 393 | const [retrying, setRetrying] = useState(false); | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 394 | const after = useRef(0); |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 395 | const runningNow = useRef(running); |
| 396 | runningNow.current = running; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 397 | useEffect(() => { |
| 398 | after.current = 0; | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 399 | steps.current = new Map(); |
| 400 | setByStep(new Map()); | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 401 | setGrewAt(new Map()); |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 402 | }, [url]); |
| 403 | useEffect(() => { | |
| 404 | let stopped = false; | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 405 | let busy = false; |
| 406 | let failures = 0; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 407 | let timer: ReturnType<typeof setTimeout> | undefined; |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 408 | const schedule = (delayMs: number | null) => { |
| 409 | if (timer) clearTimeout(timer); | |
| 410 | timer = undefined; | |
| 411 | if (stopped || delayMs === null) return; | |
| 412 | timer = setTimeout(load, delayMs); | |
| 413 | }; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 414 | const load = async () => { |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 415 | timer = undefined; |
| 416 | if (stopped || busy) return; | |
| 417 | // Hidden, a live log waits; it asks again when the tab is shown. | |
| 418 | if (live && typeof document !== "undefined" && document.visibilityState === "hidden") return; | |
| 419 | busy = true; | |
| 420 | let answer: PollAnswer; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 421 | try { |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 422 | const response = await fetch(`${url}?after=${after.current}`, { cache: "no-store" }); |
| 423 | if (!response.ok) { | |
| 424 | answer = { ok: false, status: response.status, retryAfterMs: retryAfterMs(response.headers.get("retry-after")) }; | |
| 425 | } else { | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 426 | const page = (await response.json()) as { chunks: LogChunk[]; done: boolean }; |
| 427 | if (stopped) return; | |
| 428 | if (page.chunks.length > 0) { | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 429 | after.current = page.chunks[page.chunks.length - 1]!.seq; |
| 430 | const now = Date.now(); | |
| Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' | 431 | for (const chunk of page.chunks) steps.current.set(chunk.step, appendLog(steps.current.get(chunk.step) ?? emptyLog(), chunk.text)); |
| 432 | setByStep(new Map(steps.current)); | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 433 | setGrewAt((known) => { |
| 434 | const next = new Map(known); | |
| 435 | for (const chunk of page.chunks) next.set(chunk.step, now); | |
| 436 | return next; | |
| 437 | }); | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 438 | } |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 439 | answer = { ok: true, done: page.done, full: page.chunks.length >= LOG_PAGE }; |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 440 | } |
| 441 | } catch { | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 442 | answer = { ok: false }; |
| 443 | } finally { | |
| 444 | busy = false; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 445 | } |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 446 | if (stopped) return; |
| 447 | const next = nextPoll(answer, { failures, live, running: runningNow.current }); | |
| 448 | failures = next.failures; | |
| 449 | setRetrying(!answer.ok && next.delayMs !== null); | |
| 450 | schedule(next.delayMs); | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 451 | }; |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 452 | const shown = () => { |
| 453 | if (document.visibilityState === "visible" && !timer && !busy && live) load(); | |
| 454 | else if (document.visibilityState === "visible" && timer && live) schedule(0); | |
| 455 | }; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 456 | load(); |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 457 | document.addEventListener("visibilitychange", shown); |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 458 | return () => { |
| 459 | stopped = true; | |
| 460 | if (timer) clearTimeout(timer); | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 461 | document.removeEventListener("visibilitychange", shown); |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 462 | }; |
| 463 | }, [url, live]); | |
| A running job's log is live, as on GitHub. A step's output reaches its run's page within about two seconds: the runner sends what is printed every second, and the page asks every second while a step runs, every three while none does; an answer that fails (a 429, a 5xx, the network) is asked again after a wait that doubles to half a minute, or as long as Retry-After says, where before one such answer stopped the log for good and the step said "No output yet." until the page was reloaded, and a signed-out viewer's run page, refreshing beside its log, ran into the 30-a-minute limit within a minute; the live log now counts as a page, and only log downloads as costly. A hidden tab stops asking and catches up when shown, and the page says when it is reconnecting. The running step opens by itself, its timer counts, its log follows the newest line while its end is on screen and offers Jump to latest once the viewer scrolls up, a step that has printed nothing for a while says "Running · no output for 2m 10s", and the job's header names the step it is on. Colour that tools print (bold, dim, italic, underline, the 16 colours as theme tokens for light and dark, 256 colours and 24-bit colour, text and background) is drawn as printed, other escape codes are dropped and links keep their text, and search reads the plain text and keeps the colours around what it marks. Every line is stamped with the time it was printed, as GitHub's raw logs are, so log.txt and the run's zip have a time at the start of each line, and a clock button beside Search logs shows the times, remembered by the browser. Set up job now gives the runner's version and name, the operating system, the runner image, the machine's architecture, CPUs and memory, the token's permissions under G1T_TOKEN Permissions, the secret source and the actions it downloads; each run step opens with GitHub's folded Run group, the script in cyan, the shell's whole path and the step's env, each uses step with its with inputs (an action's defaults among them) and env; cache and artifact transfers say how far they have got every five seconds; and a last Complete job step sets the job's outputs and stops what the steps left running, found by RUNNER_TRACKING_ID as GitHub's runner finds it. Two batches of a job's log written at once can no longer reach for the same seq and lose one. The Actions guide and the rate limits page say so. | 464 | return { byStep, grewAt, retrying }; |
| 465 | } | |
| 466 | ||
| 467 | /** The most chunks one answer of the log carries (services/actions, `logs`). */ | |
| 468 | const LOG_PAGE = 500; | |
| 469 | ||
| 470 | /** Now, as a number, again every `everyMs` while `on`: for times that count up as you watch. */ | |
| 471 | export function useNow(on: boolean, everyMs = 1000): number { | |
| 472 | const [now, setNow] = useState(() => Date.now()); | |
| 473 | useEffect(() => { | |
| 474 | if (!on) return; | |
| 475 | setNow(Date.now()); | |
| 476 | const timer = setInterval(() => setNow(Date.now()), everyMs); | |
| 477 | return () => clearInterval(timer); | |
| 478 | }, [on, everyMs]); | |
| 479 | return now; | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 480 | } |
| 481 | ||
| 482 | const NOTE_ICON = { | |
| 483 | info: <Info size={14} className="mt-0.5 shrink-0 text-muted" />, | |
| 484 | warning: <AlertTriangle size={14} className="mt-0.5 shrink-0 text-warn" />, | |
| 485 | unsupported: <CircleSlash size={14} className="mt-0.5 shrink-0 text-danger" />, | |
| 486 | }; | |
| 487 | ||
| 488 | /** What in a workflow runs differently on g1t, worst first. */ | |
| 489 | export function Notes({ notes }: { notes: WorkflowNote[] }) { | |
| 490 | if (notes.length === 0) return null; | |
| 491 | const order = { unsupported: 0, warning: 1, info: 2 }; | |
| 492 | const sorted = [...notes].sort((a, b) => order[a.severity] - order[b.severity]); | |
| 493 | const blocking = notes.filter((n) => n.severity === "unsupported").length; | |
| 494 | return ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 495 | <Card asChild className="group"> |
| 496 | <details open={blocking > 0}> | |
| 497 | <summary className="cursor-pointer list-none px-4 py-2.5 text-sm"> | |
| 498 | <span className="font-medium">How this runs on g1t</span> | |
| 499 | <span className="text-muted"> | |
| 500 | {" · "} | |
| 501 | {blocking > 0 ? `${blocking} thing${blocking === 1 ? "" : "s"} g1t cannot run yet` : `${notes.length} note${notes.length === 1 ? "" : "s"}`} | |
| 502 | </span> | |
| 503 | </summary> | |
| 504 | <ul className="space-y-2 border-t border-line px-4 py-3 text-sm"> | |
| 505 | {sorted.map((note, index) => ( | |
| 506 | <li key={index} className="flex gap-2"> | |
| 507 | {NOTE_ICON[note.severity]} | |
| 508 | <span> | |
| 509 | {note.job && <span className="font-mono text-xs text-muted">{note.job}: </span>} | |
| 510 | {note.message} | |
| 511 | </span> | |
| 512 | </li> | |
| 513 | ))} | |
| 514 | </ul> | |
| 515 | </details> | |
| 516 | </Card> | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 517 | ); |
| 518 | } | |
| 519 | ||
| 520 | ||
| 521 | /** What workflow runs said about a pull request's head, each linking to its run. */ | |
| 522 | export function WorkflowStatuses({ statuses }: { statuses: CommitStatus[] }) { | |
| 523 | if (statuses.length === 0) return null; | |
| 524 | const standing = (state: CommitStatus["state"]) => | |
| 525 | state === "pending" | |
| 526 | ? { status: "in_progress", conclusion: null } | |
| 527 | : { status: "completed", conclusion: (state === "success" ? "success" : "failure") as Conclusion }; | |
| 528 | const failed = statuses.filter((s) => s.state === "failure" || s.state === "error").length; | |
| 529 | const pending = statuses.filter((s) => s.state === "pending").length; | |
| 530 | return ( | |
| The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after. | 531 | <Card asChild className="p-4"> |
| 532 | <section> | |
| 533 | <h3 className="text-sm font-medium"> | |
| 534 | {failed > 0 ? `${failed} workflow${failed === 1 ? "" : "s"} failed` : pending > 0 ? "Workflows running" : "Workflows passed"} | |
| 535 | </h3> | |
| 536 | <ul className="mt-3 space-y-2 text-sm"> | |
| 537 | {statuses.map((status) => { | |
| 538 | const path = status.targetUrl?.replace(/^https:\/\/g1t\.sh/, "") ?? null; | |
| 539 | const row = ( | |
| 540 | <> | |
| 541 | <StatusIcon {...standing(status.state)} size={14} /> | |
| 542 | <span className="min-w-0 truncate">{status.context}</span> | |
| 543 | </> | |
| 544 | ); | |
| 545 | return ( | |
| 546 | <li key={status.context}> | |
| 547 | {path ? ( | |
| 548 | <Hint label={status.description}> | |
| 549 | <Link to={path} className="flex items-center gap-2 hover:text-fg"> | |
| 550 | {row} | |
| 551 | </Link> | |
| 552 | </Hint> | |
| 553 | ) : ( | |
| 554 | <span className="flex items-center gap-2">{row}</span> | |
| 555 | )} | |
| 556 | </li> | |
| 557 | ); | |
| 558 | })} | |
| 559 | </ul> | |
| 560 | </section> | |
| 561 | </Card> | |
| Packages lists like a registry: Type, search, Visibility and Sort in one bar, richer rows, and a registry to choose from to start | 562 | ); |
| 563 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.