@hanzo/build 0.2.29 → 0.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/coding.d.ts +11 -1
- package/lib/api/coding.js +17 -2
- package/lib/api/turn.d.ts +2 -0
- package/lib/api/turn.js +16 -1
- package/lib/desk.js +7 -6
- package/lib/run.js +8 -3
- package/lib/transcript.js +1 -1
- package/package.json +1 -1
package/lib/api/coding.d.ts
CHANGED
|
@@ -78,7 +78,11 @@ export interface Earlier {
|
|
|
78
78
|
/** Its work is on its own branch (turn.ts `settled`). */
|
|
79
79
|
pushed: boolean;
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
81
|
+
/**
|
|
82
|
+
* The earlier run's ask, without the codebase its title leads with (`universe:
|
|
83
|
+
* add the widget`), or the `new project:` the platform titles a run that made its
|
|
84
|
+
* codebase with (apps/coding start.go).
|
|
85
|
+
*/
|
|
82
86
|
export declare function headline(title: string, repo: string): string;
|
|
83
87
|
/**
|
|
84
88
|
* A new run continuing an earlier one in its mode: what the person said, and
|
|
@@ -89,3 +93,9 @@ export declare function headline(title: string, repo: string): string;
|
|
|
89
93
|
export declare function followUp(e: Earlier, said: string): Ask;
|
|
90
94
|
/** A build of what a plan run answered: its ask as the title, and the plan to carry out. */
|
|
91
95
|
export declare function approve(e: Earlier, plan: string): Ask;
|
|
96
|
+
/**
|
|
97
|
+
* The same ask again, where the earlier run worked and in its mode: what a failed
|
|
98
|
+
* run's Try again starts. `ask` is the person's words as the run recorded them;
|
|
99
|
+
* with none recorded, its title's.
|
|
100
|
+
*/
|
|
101
|
+
export declare function retry(e: Earlier, ask: string): Ask;
|
package/lib/api/coding.js
CHANGED
|
@@ -63,11 +63,18 @@ export async function start(t, ask) {
|
|
|
63
63
|
target: s('targetId'),
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* The earlier run's ask, without the codebase its title leads with (`universe:
|
|
68
|
+
* add the widget`), or the `new project:` the platform titles a run that made its
|
|
69
|
+
* codebase with (apps/coding start.go).
|
|
70
|
+
*/
|
|
67
71
|
export function headline(title, repo) {
|
|
68
72
|
const name = repo.split('/').filter(Boolean).pop() ?? '';
|
|
69
73
|
const t = title.trim();
|
|
70
|
-
|
|
74
|
+
for (const lead of [name, 'new project'])
|
|
75
|
+
if (lead && t.startsWith(`${lead}: `))
|
|
76
|
+
return t.slice(lead.length + 2).trim();
|
|
77
|
+
return t;
|
|
71
78
|
}
|
|
72
79
|
/**
|
|
73
80
|
* The same codebase, place and project as the earlier run, starting from its
|
|
@@ -112,3 +119,11 @@ export function approve(e, plan) {
|
|
|
112
119
|
const ask = `Carry out this plan:\n\n${plan.trim()}`;
|
|
113
120
|
return { ...from(e), prompt: head ? `${head}\n\n${ask}` : ask, mode: 'build' };
|
|
114
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* The same ask again, where the earlier run worked and in its mode: what a failed
|
|
124
|
+
* run's Try again starts. `ask` is the person's words as the run recorded them;
|
|
125
|
+
* with none recorded, its title's.
|
|
126
|
+
*/
|
|
127
|
+
export function retry(e, ask) {
|
|
128
|
+
return { ...from(e), prompt: ask.trim() || headline(e.title, e.repo), mode: modeOf(e.mode) };
|
|
129
|
+
}
|
package/lib/api/turn.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export interface Outcome {
|
|
|
56
56
|
* the branch and the pull request are read from the run's record (`pull`).
|
|
57
57
|
*/
|
|
58
58
|
export declare function outcome(events: Pick<Event, 'kind' | 'payload' | 'seq'>[]): Outcome;
|
|
59
|
+
/** Why the run failed, as its last status says it, or '' when that status is not a failure. */
|
|
60
|
+
export declare function failure(events: Pick<Event, 'kind' | 'payload' | 'seq'>[]): string;
|
|
59
61
|
/** The turns, ordered and deduped: a recorded read and the live feed overlap. */
|
|
60
62
|
export declare function merge<T extends Pick<Event, 'id' | 'seq'>>(...lists: T[][]): T[];
|
|
61
63
|
/** The actor, short enough to label a block: `hanzo/2d4d67ab`, not the whole subject. */
|
package/lib/api/turn.js
CHANGED
|
@@ -89,7 +89,9 @@ export function plain(error) {
|
|
|
89
89
|
if (!error)
|
|
90
90
|
return 'The run hit an error';
|
|
91
91
|
const ours = /providers_exhausted|every provider refused|temporarily unavailable/i.test(error);
|
|
92
|
-
if (
|
|
92
|
+
if (/free sandboxes .*are used/i.test(error))
|
|
93
|
+
return 'This plan’s free sandboxes are used for now. Add credit to keep building, or try again later.';
|
|
94
|
+
if (!ours && /\b402 Payment Required|insufficient (credit|balance)|top up/i.test(error))
|
|
93
95
|
return 'Your balance is out of credit. Add credit, then try again.';
|
|
94
96
|
if (ours || /\b(429 Too Many Requests|503 Service Unavailable)|stream disconnected|retries exhausted|rate limit/i.test(error))
|
|
95
97
|
return 'The model did not answer. Try again in a moment, or pick another model.';
|
|
@@ -208,6 +210,19 @@ export function outcome(events) {
|
|
|
208
210
|
}
|
|
209
211
|
return out;
|
|
210
212
|
}
|
|
213
|
+
/** Why the run failed, as its last status says it, or '' when that status is not a failure. */
|
|
214
|
+
export function failure(events) {
|
|
215
|
+
let out = '';
|
|
216
|
+
for (const e of [...events].sort((a, b) => a.seq - b.seq)) {
|
|
217
|
+
if (e.kind !== 'status')
|
|
218
|
+
continue;
|
|
219
|
+
const b = decode(e.payload);
|
|
220
|
+
if (!b || typeof b === 'string' || !str(b.status))
|
|
221
|
+
continue;
|
|
222
|
+
out = str(b.status) === 'error' ? str(b.error) || 'The run hit an error' : '';
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
211
226
|
/** The turns, ordered and deduped: a recorded read and the live feed overlap. */
|
|
212
227
|
export function merge(...lists) {
|
|
213
228
|
const seen = new Map();
|
package/lib/desk.js
CHANGED
|
@@ -9,7 +9,7 @@ import { ExternalLink, Info, MoreHorizontal, PanelRightClose, RefreshCcw } from
|
|
|
9
9
|
import { Button, DropdownMenu } from '@hanzo/ui';
|
|
10
10
|
import { PreviewFrame } from '@hanzo/ui/agents';
|
|
11
11
|
import { useRef, useState } from 'react';
|
|
12
|
-
import { shell } from './api/turn.js';
|
|
12
|
+
import { failure, shell } from './api/turn.js';
|
|
13
13
|
import { Door } from './door.js';
|
|
14
14
|
import { Environment } from './environment.js';
|
|
15
15
|
import { Artifacts, Files } from './files.js';
|
|
@@ -24,8 +24,9 @@ export function Desk({ id, repo, branch, base, environment, mode, pr, title, pro
|
|
|
24
24
|
const tab = picked ?? (mode === 'setup' ? 'environment' : site ? 'browser' : 'terminal');
|
|
25
25
|
const name = repo.split('/').filter(Boolean).pop() || repo;
|
|
26
26
|
const lines = shell(events);
|
|
27
|
+
const failed = failure(events);
|
|
27
28
|
const bleed = BLEED.includes(tab);
|
|
28
|
-
return (_jsxs(YStack, { role: "complementary", "aria-label": "Run details", flex: 1, minW: 0, minH: 0, borderLeftWidth: 1, borderColor: "$borderColor", display: "none", $md: { display: 'flex' }, children: [_jsxs(XStack, { px: "$2", pt: "$2", gap: "$1", borderBottomWidth: 1, borderColor: "$borderColor", items: "center", children: [_jsxs(XStack, { flex: 1, minW: 0, gap: "$1", overflow: "scroll", children: [_jsx(TabButton, { id: "browser", tab: tab, onPick: setTab, children: "Browser" }), _jsx(TabButton, { id: "desktop", tab: tab, onPick: setTab, children: "Desktop" }), _jsx(TabButton, { id: "terminal", tab: tab, onPick: setTab, children: "Terminal" }), _jsx(TabButton, { id: "artifacts", tab: tab, onPick: setTab, children: "Artifacts" }), _jsx(TabButton, { id: "files", tab: tab, onPick: setTab, children: "Files" }), _jsx(TabButton, { id: "git", tab: tab, onPick: setTab, children: "Git" }), _jsx(TabButton, { id: "environment", tab: tab, onPick: setTab, children: "Environment" })] }), _jsx(XStack, { render: "button", "aria-label": "Hide the side pane", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, onPress: onHide, children: _jsx(PanelRightClose, { size: 16 }) }), _jsx(DropdownMenu, { trigger: _jsx(XStack, { render: "button", "aria-label": "Run actions", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(MoreHorizontal, { size: 16 }) }), items: [...menu, { key: 'details', label: 'Details', icon: _jsx(Info, { size: 16 }), onSelect: () => setTab('environment') }] })] }), _jsxs(YStack, { flex: 1, minH: 0, overflow: bleed ? 'hidden' : 'scroll', px: bleed ? 0 : '$3', py: bleed ? 0 : '$3', children: [tab === 'environment' ? (_jsxs(YStack, { gap: "$2", children: [_jsx(Environment, { repo: name, busy: live && mode === 'setup' }), _jsx(SizableText, { size: "$2", color: "$ink", pt: "$4", children: "This run" }), _jsx(Fact, { label: "Run", value: id }), _jsx(Fact, { label: "Repository", value: name || '—' }), _jsx(Fact, { label: "Branch", value: branch || '—' }), _jsx(Fact, { label: "Base", value: base || '—' }), _jsx(Fact, { label: "Runs on", value: environment || 'sandbox' }), _jsx(Fact, { label: "Mode", value: mode || 'build' })] })) : null, tab === 'browser' ? _jsx(Browser, { site: site, live: live }, published) : null, tab === 'git' ? _jsx(Git, { session: id, title: title, live: live }) : null, tab === 'desktop' ? _jsx(Door, { which: "screen", sandbox: sandbox, live: live, session: id }) : null, tab === 'terminal' ? (_jsx(Terminal, { id: id, sandbox: sandbox, lines: lines, live: live, refused: refused, retry: retry, onRetry: onRetry })) : null, tab === 'artifacts' ? (_jsx(Artifacts, { repo: repo, branch: branch, mode: mode, project: project, site: site, pr: pr, onEnvironment: () => setTab('environment') })) : null, tab === 'files' ? _jsx(Files, { session: id, repo: repo, branch: branch, sandbox: sandbox, live: live }) : null] })] }));
|
|
29
|
+
return (_jsxs(YStack, { role: "complementary", "aria-label": "Run details", flex: 1, minW: 0, minH: 0, borderLeftWidth: 1, borderColor: "$borderColor", display: "none", $md: { display: 'flex' }, children: [_jsxs(XStack, { px: "$2", pt: "$2", gap: "$1", borderBottomWidth: 1, borderColor: "$borderColor", items: "center", children: [_jsxs(XStack, { flex: 1, minW: 0, gap: "$1", overflow: "scroll", children: [_jsx(TabButton, { id: "browser", tab: tab, onPick: setTab, children: "Browser" }), _jsx(TabButton, { id: "desktop", tab: tab, onPick: setTab, children: "Desktop" }), _jsx(TabButton, { id: "terminal", tab: tab, onPick: setTab, children: "Terminal" }), _jsx(TabButton, { id: "artifacts", tab: tab, onPick: setTab, children: "Artifacts" }), _jsx(TabButton, { id: "files", tab: tab, onPick: setTab, children: "Files" }), _jsx(TabButton, { id: "git", tab: tab, onPick: setTab, children: "Git" }), _jsx(TabButton, { id: "environment", tab: tab, onPick: setTab, children: "Environment" })] }), _jsx(XStack, { render: "button", "aria-label": "Hide the side pane", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, onPress: onHide, children: _jsx(PanelRightClose, { size: 16 }) }), _jsx(DropdownMenu, { trigger: _jsx(XStack, { render: "button", "aria-label": "Run actions", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(MoreHorizontal, { size: 16 }) }), items: [...menu, { key: 'details', label: 'Details', icon: _jsx(Info, { size: 16 }), onSelect: () => setTab('environment') }] })] }), _jsxs(YStack, { flex: 1, minH: 0, overflow: bleed ? 'hidden' : 'scroll', px: bleed ? 0 : '$3', py: bleed ? 0 : '$3', children: [tab === 'environment' ? (_jsxs(YStack, { gap: "$2", children: [_jsx(Environment, { repo: name, busy: live && mode === 'setup' }), _jsx(SizableText, { size: "$2", color: "$ink", pt: "$4", children: "This run" }), _jsx(Fact, { label: "Run", value: id }), _jsx(Fact, { label: "Repository", value: name || '—' }), _jsx(Fact, { label: "Branch", value: branch || '—' }), _jsx(Fact, { label: "Base", value: base || '—' }), _jsx(Fact, { label: "Runs on", value: environment || 'sandbox' }), _jsx(Fact, { label: "Mode", value: mode || 'build' })] })) : null, tab === 'browser' ? _jsx(Browser, { site: site, live: live }, published) : null, tab === 'git' ? _jsx(Git, { session: id, title: title, live: live }) : null, tab === 'desktop' ? _jsx(Door, { which: "screen", sandbox: sandbox, live: live, session: id }) : null, tab === 'terminal' ? (_jsx(Terminal, { id: id, sandbox: sandbox, lines: lines, live: live, failed: failed, refused: refused, retry: retry, onRetry: onRetry })) : null, tab === 'artifacts' ? (_jsx(Artifacts, { repo: repo, branch: branch, mode: mode, project: project, site: site, pr: pr, onEnvironment: () => setTab('environment') })) : null, tab === 'files' ? _jsx(Files, { session: id, repo: repo, branch: branch, sandbox: sandbox, live: live }) : null] })] }));
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
32
|
* The Browser tab: the page where the run published its work, framed, with its
|
|
@@ -49,15 +50,15 @@ const mono = { fontFamily: 'var(--f-mono, ui-monospace, monospace)', whiteSpace:
|
|
|
49
50
|
* outlive the sandbox. The shell is a tmux session named for the run, so a
|
|
50
51
|
* reopened tab reattaches to it.
|
|
51
52
|
*/
|
|
52
|
-
function Terminal({ id, sandbox, lines, live, refused, retry, onRetry, }) {
|
|
53
|
+
function Terminal({ id, sandbox, lines, live, failed, refused, retry, onRetry, }) {
|
|
53
54
|
const reachable = live && Boolean(sandbox);
|
|
54
55
|
// The shell while there is one, unless the log was asked for.
|
|
55
56
|
const [log, setLog] = useState(false);
|
|
56
57
|
const showShell = reachable && !log;
|
|
57
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, bg: "$background", children: [_jsxs(XStack, { px: "$2", py: "$1.5", gap: "$1", borderBottomWidth: 1, borderColor: "$borderColor", items: "center", children: [reachable ? (_jsx(XStack, { render: "button", "aria-label": "Shell", onPress: () => setLog(false), px: "$2", py: "$1", rounded: "$2", bg: showShell ? '$hover' : 'transparent', children: _jsx(SizableText, { size: "$1", color: showShell ? '$ink' : '$soft', children: "Shell" }) })) : null, _jsx(XStack, { render: "button", "aria-label": "Agent log", onPress: () => setLog(true), px: "$2", py: "$1", rounded: "$2", bg: showShell ? 'transparent' : '$hover', children: _jsx(SizableText, { size: "$1", color: showShell ? '$soft' : '$ink', children: "Agent log" }) })] }), showShell ? _jsx(Door, { which: "terminal", sandbox: sandbox, live: live, session: `run-${id.replace(/^sess_/, '').slice(0, 12)}` }) : _jsx(Log, { lines: lines, live: live, refused: refused, retry: retry, onRetry: onRetry })] }));
|
|
58
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, bg: "$background", children: [_jsxs(XStack, { px: "$2", py: "$1.5", gap: "$1", borderBottomWidth: 1, borderColor: "$borderColor", items: "center", children: [reachable ? (_jsx(XStack, { render: "button", "aria-label": "Shell", onPress: () => setLog(false), px: "$2", py: "$1", rounded: "$2", bg: showShell ? '$hover' : 'transparent', children: _jsx(SizableText, { size: "$1", color: showShell ? '$ink' : '$soft', children: "Shell" }) })) : null, _jsx(XStack, { render: "button", "aria-label": "Agent log", onPress: () => setLog(true), px: "$2", py: "$1", rounded: "$2", bg: showShell ? 'transparent' : '$hover', children: _jsx(SizableText, { size: "$1", color: showShell ? '$soft' : '$ink', children: "Agent log" }) })] }), showShell ? _jsx(Door, { which: "terminal", sandbox: sandbox, live: live, session: `run-${id.replace(/^sess_/, '').slice(0, 12)}` }) : _jsx(Log, { lines: lines, live: live, failed: failed, refused: refused, retry: retry, onRetry: onRetry })] }));
|
|
58
59
|
}
|
|
59
|
-
function Log({ lines, live, refused, retry, onRetry, }) {
|
|
60
|
+
function Log({ lines, live, failed, refused, retry, onRetry, }) {
|
|
60
61
|
const prompt = 'workspace';
|
|
61
62
|
const idle = lines.length === 0 && !live;
|
|
62
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(YStack, { flex: 1, minH: 0, overflow: "scroll", px: "$3", py: "$3", gap: "$1", children: [idle ? (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", py: "$8", children: [_jsx(SizableText, { size: "$2", color: "$soft", style: { textAlign: 'center' }, children: refused || 'This run finished without writing a command.' }), refused ? (_jsx(Button, { size: "sm", variant: "outline", onPress: onRetry, children: retry })) : null] })) : (lines.map((line, i) => line.role === 'cmd' ? (_jsxs(XStack, { gap: "$2", children: [_jsx(SizableText, { size: "$1", color: "$soft", style: mono, children: `${prompt} $` }), _jsx(SizableText, { flex: 1, size: "$1", color: "$ink", style: mono, children: line.text })] }, i)) : (_jsx(SizableText, { size: "$1", color: "$ink", style: mono, children: line.text }, i)))), live ? (_jsxs(XStack, { gap: "$2", items: "center", children: [_jsx(SizableText, { size: "$1", color: "$soft", style: mono, children: `${prompt} $` }), _jsx(YStack, { width: 7, height: 14, bg: "$ink" })] })) : null] }), _jsx(XStack, { px: "$3", py: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: _jsx(SizableText, { size: "$1", color: "$soft", children: "Commands appear here as the run writes them." }) })] }));
|
|
63
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsxs(YStack, { flex: 1, minH: 0, overflow: "scroll", px: "$3", py: "$3", gap: "$1", children: [idle ? (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", py: "$8", children: [_jsx(SizableText, { size: "$2", color: "$soft", style: { textAlign: 'center' }, children: refused || (failed ? `This run stopped before its first command: ${failed}` : 'This run finished without writing a command.') }), refused ? (_jsx(Button, { size: "sm", variant: "outline", onPress: onRetry, children: retry })) : null] })) : (lines.map((line, i) => line.role === 'cmd' ? (_jsxs(XStack, { gap: "$2", children: [_jsx(SizableText, { size: "$1", color: "$soft", style: mono, children: `${prompt} $` }), _jsx(SizableText, { flex: 1, size: "$1", color: "$ink", style: mono, children: line.text })] }, i)) : (_jsx(SizableText, { size: "$1", color: "$ink", style: mono, children: line.text }, i)))), live ? (_jsxs(XStack, { gap: "$2", items: "center", children: [_jsx(SizableText, { size: "$1", color: "$soft", style: mono, children: `${prompt} $` }), _jsx(YStack, { width: 7, height: 14, bg: "$ink" })] })) : null] }), _jsx(XStack, { px: "$3", py: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: _jsx(SizableText, { size: "$1", color: "$soft", children: "Commands appear here as the run writes them." }) })] }));
|
|
63
64
|
}
|
package/lib/run.js
CHANGED
|
@@ -30,7 +30,7 @@ import { Steer } from '@hanzo/ui/agents';
|
|
|
30
30
|
import { Composer } from '@hanzo/ui/chat';
|
|
31
31
|
import { ChipSelect } from '@hanzo/ui/product';
|
|
32
32
|
import { useEffect, useMemo, useState } from 'react';
|
|
33
|
-
import { approve, followUp, start } from './api/coding.js';
|
|
33
|
+
import { approve, followUp, retry, start } from './api/coding.js';
|
|
34
34
|
import { ENSO, label as named, models } from './api/models.js';
|
|
35
35
|
import { list, message, pause, publish, rename, resume, stop, story, took } from './api/sessions.js';
|
|
36
36
|
import { answer, cards, outcome, pull, settled, steps } from './api/turn.js';
|
|
@@ -115,6 +115,10 @@ export function Run({ id }) {
|
|
|
115
115
|
}
|
|
116
116
|
: null;
|
|
117
117
|
const planned = mode === 'plan' && finished && state === 'done' ? answer(events) : '';
|
|
118
|
+
// A failed run is tried again with the person's words as it recorded them.
|
|
119
|
+
const failed = finished && state === 'error';
|
|
120
|
+
const asked = shown.find((c) => c.kind === 'said' && c.who === 'person');
|
|
121
|
+
const again = asked && asked.kind === 'said' ? asked.text : '';
|
|
118
122
|
// What the recorded read left out: it carries the latest fifty turns.
|
|
119
123
|
const hidden = Math.max(0, (detail.value?.events ?? 0) - (detail.value?.recent.length ?? 0));
|
|
120
124
|
// Only a run still working is on a step.
|
|
@@ -163,7 +167,8 @@ export function Run({ id }) {
|
|
|
163
167
|
const carry = async (said, how = 'follow') => {
|
|
164
168
|
if (!earlier)
|
|
165
169
|
throw new Error('This run is still being read');
|
|
166
|
-
const
|
|
170
|
+
const ask = how === 'approve' ? approve(earlier, said) : how === 'retry' ? retry(earlier, said) : followUp(earlier, said);
|
|
171
|
+
const run = await start(t, tuned(ask));
|
|
167
172
|
if (paused)
|
|
168
173
|
await stop(t, id, 'Continued in a follow-up run').catch(() => undefined);
|
|
169
174
|
setDraft('');
|
|
@@ -283,7 +288,7 @@ export function Run({ id }) {
|
|
|
283
288
|
? setup
|
|
284
289
|
? 'Setting up environment'
|
|
285
290
|
: 'Starting…'
|
|
286
|
-
: 'Waiting for the run’s first step…' })) : (_jsxs(YStack, { gap: "$3", items: "flex-start", children: [_jsx(SizableText, { size: "$2", color: "$soft", children: "Sign in to follow this run." }), _jsx(Button, { size: "sm", onPress: () => host.signIn?.(), children: "Sign in" })] })) }), _jsxs(YStack, { pb: "$3", pt: "$2", gap: "$2", children: [end.problem ? (_jsx(SizableText, { size: "$1", color: "$soft", children: "The branch is pushed, and the pull request could not be opened." })) : null, note || (signed && refused) ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note || refused })) : null, plan.length ? (_jsxs(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: "$3", overflow: "hidden", children: [_jsxs(XStack, { render: "button", "aria-label": `Steps · ${plan.length}`, "aria-expanded": unfolded, onPress: () => setUnfolded(!unfolded), px: "$3", py: "$1.5", gap: "$2", items: "center", hoverStyle: { bg: '$hover' }, children: [unfolded ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 }), _jsx(SizableText, { size: "$2", color: "$ink", children: `Steps · ${plan.length}` }), current && !unfolded ? (_jsx(SizableText, { flex: 1, minW: 0, size: "$1", color: "$soft", numberOfLines: 1, style: { textAlign: 'right' }, children: current.name })) : null] }), unfolded
|
|
291
|
+
: 'Waiting for the run’s first step…' })) : (_jsxs(YStack, { gap: "$3", items: "flex-start", children: [_jsx(SizableText, { size: "$2", color: "$soft", children: "Sign in to follow this run." }), _jsx(Button, { size: "sm", onPress: () => host.signIn?.(), children: "Sign in" })] })) }), _jsxs(YStack, { pb: "$3", pt: "$2", gap: "$2", children: [end.problem ? (_jsx(SizableText, { size: "$1", color: "$soft", children: "The branch is pushed, and the pull request could not be opened." })) : null, note || (signed && refused) ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note || refused })) : null, failed ? (_jsx(XStack, { items: "center", gap: "$2", children: _jsx(Button, { size: "sm", disabled: busy, onPress: () => void act(() => carry(again, 'retry')), children: busy ? 'Starting…' : 'Try again' }) })) : null, plan.length ? (_jsxs(YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: "$3", overflow: "hidden", children: [_jsxs(XStack, { render: "button", "aria-label": `Steps · ${plan.length}`, "aria-expanded": unfolded, onPress: () => setUnfolded(!unfolded), px: "$3", py: "$1.5", gap: "$2", items: "center", hoverStyle: { bg: '$hover' }, children: [unfolded ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 }), _jsx(SizableText, { size: "$2", color: "$ink", children: `Steps · ${plan.length}` }), current && !unfolded ? (_jsx(SizableText, { flex: 1, minW: 0, size: "$1", color: "$soft", numberOfLines: 1, style: { textAlign: 'right' }, children: current.name })) : null] }), unfolded
|
|
287
292
|
? plan.map((s) => {
|
|
288
293
|
const on = s === current;
|
|
289
294
|
return (_jsxs(XStack, { items: "center", gap: "$2", px: "$3", py: "$1.5", borderTopWidth: 1, borderColor: "$borderColor", children: [_jsx(SizableText, { size: "$2", color: s.done || on ? '$ink' : '$soft', children: s.done ? '✓' : on ? '●' : '○' }), _jsx(SizableText, { size: "$2", color: s.done ? '$soft' : '$ink', numberOfLines: 1, children: s.name })] }, s.name));
|
package/lib/transcript.js
CHANGED
|
@@ -50,7 +50,7 @@ export function Transcript({ cards, live, empty, header, onApprove, approving =
|
|
|
50
50
|
case 'edit':
|
|
51
51
|
return (_jsx(Step, { name: c.files.length === 1 ? 'Edited' : c.files.length ? `Edited ${c.files.length} files` : 'Edited files', detail: c.files.join(', '), status: state(c.ran), "aria-label": `Edited ${c.files.join(', ') || 'files'}`, children: c.patch ? (_jsx(YStack, { overflow: "scroll", maxH: 480, children: _jsx(Patch, { text: c.patch }) })) : (_jsx(SizableText, { size: "$1", color: "$soft", children: c.files.length ? c.files.join('\n') : 'The harness named no files.' })) }, c.key));
|
|
52
52
|
case 'step':
|
|
53
|
-
return (_jsx(Step, { name: c.name, detail: c.detail, status: state(c.ran), "aria-label": `${c.name} ${c.detail}`.trim(), children: c.output.trim() ? (_jsx(Code, { language: "output", value: c.output, children: tail(c.output.replace(/\n+$/, '')) })) : undefined }, c.key));
|
|
53
|
+
return (_jsx(Step, { name: c.name, detail: c.detail, status: state(c.ran), defaultOpen: c.name === 'Error', "aria-label": `${c.name} ${c.detail}`.trim(), children: c.name === 'Error' ? (_jsx(SizableText, { size: "$2", color: "$ink", style: { ...mono, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: c.output })) : c.output.trim() ? (_jsx(Code, { language: "output", value: c.output, children: tail(c.output.replace(/\n+$/, '')) })) : undefined }, c.key));
|
|
54
54
|
case 'note':
|
|
55
55
|
return (_jsx(SizableText, { size: "$1", color: "$soft", role: "note", children: c.text }, c.key));
|
|
56
56
|
case 'plan':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/build",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"description": "Hanzo Build — say what you want, watch it built, deployed and running. The builder as a component, mounted by the Hanzo app at hanzo.ai.",
|