@hanzo/build 0.2.1 → 0.2.2
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 +10 -13
- package/lib/api/coding.js +12 -15
- package/lib/api/turn.js +3 -0
- package/lib/landing.js +1 -1
- package/lib/project.js +1 -1
- package/package.json +1 -1
package/lib/api/coding.d.ts
CHANGED
|
@@ -10,22 +10,19 @@
|
|
|
10
10
|
* id routes it to one of the org's machines, and `routed` says whether it got
|
|
11
11
|
* there.
|
|
12
12
|
*
|
|
13
|
-
* `mode
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* `mode` is what the run may do: `build` edits, commits and pushes its branch;
|
|
14
|
+
* `plan` reads the repository with a credential that can only read and answers
|
|
15
|
+
* with a plan, as its final status. A plan runs in the platform's sandbox only —
|
|
16
|
+
* a claimed machine clones and pushes with its own credential — so a plan
|
|
17
|
+
* routed to one is refused, here before sending as on the platform.
|
|
18
|
+
*
|
|
19
|
+
* `model` and `effort` are taken by the dev harness the builder runs (and by
|
|
20
|
+
* codex); the builder names no other harness, so they are always honoured.
|
|
17
21
|
*/
|
|
18
22
|
import { type Target } from './call.ts';
|
|
19
23
|
export type Mode = 'build' | 'plan';
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
* a plan run would edit, commit and push like any other — on a machine, with
|
|
23
|
-
* that machine's credentials. Until the platform takes `plan`, a plan ask is
|
|
24
|
-
* refused here rather than sent to become a build.
|
|
25
|
-
*/
|
|
26
|
-
export declare const HONOURED: readonly Mode[];
|
|
27
|
-
/** Why a mode cannot be asked for yet, or '' when it can. */
|
|
28
|
-
export declare const unhonoured: (mode: Mode | undefined) => string;
|
|
24
|
+
/** Why this mode cannot run where it is sent, or '' when it can. */
|
|
25
|
+
export declare const unhonoured: (mode: Mode | undefined, target?: string) => string;
|
|
29
26
|
export interface Ask {
|
|
30
27
|
/** The task, in the words you would use with a colleague. */
|
|
31
28
|
prompt: string;
|
package/lib/api/coding.js
CHANGED
|
@@ -10,22 +10,19 @@
|
|
|
10
10
|
* id routes it to one of the org's machines, and `routed` says whether it got
|
|
11
11
|
* there.
|
|
12
12
|
*
|
|
13
|
-
* `mode
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* `mode` is what the run may do: `build` edits, commits and pushes its branch;
|
|
14
|
+
* `plan` reads the repository with a credential that can only read and answers
|
|
15
|
+
* with a plan, as its final status. A plan runs in the platform's sandbox only —
|
|
16
|
+
* a claimed machine clones and pushes with its own credential — so a plan
|
|
17
|
+
* routed to one is refused, here before sending as on the platform.
|
|
18
|
+
*
|
|
19
|
+
* `model` and `effort` are taken by the dev harness the builder runs (and by
|
|
20
|
+
* codex); the builder names no other harness, so they are always honoured.
|
|
17
21
|
*/
|
|
18
22
|
import { call, Refusal } from './call.js';
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* that machine's credentials. Until the platform takes `plan`, a plan ask is
|
|
23
|
-
* refused here rather than sent to become a build.
|
|
24
|
-
*/
|
|
25
|
-
export const HONOURED = ['build'];
|
|
26
|
-
/** Why a mode cannot be asked for yet, or '' when it can. */
|
|
27
|
-
export const unhonoured = (mode) => mode && !HONOURED.includes(mode)
|
|
28
|
-
? 'Plan needs the platform’s plan mode, which the coding API does not take yet — this ask would build. Switch to Build, or wait for Plan.'
|
|
23
|
+
/** Why this mode cannot run where it is sent, or '' when it can. */
|
|
24
|
+
export const unhonoured = (mode, target) => mode === 'plan' && target?.trim()
|
|
25
|
+
? 'A plan runs in the Hanzo sandbox: a machine clones and pushes with its own credential. Choose Default, or switch to Build.'
|
|
29
26
|
: '';
|
|
30
27
|
/** The request body: only what was asked, with no empty strings sent as values. */
|
|
31
28
|
export function body(ask) {
|
|
@@ -40,7 +37,7 @@ export function body(ask) {
|
|
|
40
37
|
export async function start(t, ask) {
|
|
41
38
|
if (!ask.prompt.trim())
|
|
42
39
|
throw new Refusal(400, 'Say what the run should do');
|
|
43
|
-
const why = unhonoured(ask.mode);
|
|
40
|
+
const why = unhonoured(ask.mode, ask.targetId);
|
|
44
41
|
if (why)
|
|
45
42
|
throw new Refusal(400, why);
|
|
46
43
|
const r = await call(t, 'POST', '/v1/agent/coding', body(ask));
|
package/lib/api/turn.js
CHANGED
|
@@ -31,6 +31,9 @@ function status(b) {
|
|
|
31
31
|
case 'routed':
|
|
32
32
|
return 'Sent to a machine';
|
|
33
33
|
case 'done': {
|
|
34
|
+
// A plan's answer IS its final status: the run read and wrote nothing.
|
|
35
|
+
if (str(b.mode) === 'plan')
|
|
36
|
+
return str(b.plan) || 'Planned — the run answered with no plan';
|
|
34
37
|
if (b.changed === false)
|
|
35
38
|
return 'Done — nothing to change';
|
|
36
39
|
const pr = str(b.pr);
|
package/lib/landing.js
CHANGED
|
@@ -128,5 +128,5 @@ export function Landing({ onStarted }) {
|
|
|
128
128
|
}),
|
|
129
129
|
}, placeholder: "Repository", disabled: !signed }), kept.repo ? _jsx(BranchSelect, { value: branch, onChange: (b) => set({ branch: b }), load: loadBranches }) : null, _jsx(Files, { files: files, onFiles: setFiles })] }));
|
|
130
130
|
const foot = (_jsxs(XStack, { flex: 1, items: "center", gap: "$2", flexWrap: "wrap", rowGap: "$1", children: [_jsx(Attach, { files: files, onFiles: setFiles, onNote: setNote }), _jsx(Dictate, { onText: (said) => setDraft((d) => (d ? `${d} ${said}` : said)), onNote: setNote }), _jsx(ModeSelect, { modes: MODES, value: kept.mode, onChange: (m) => set({ mode: m }), bg: "transparent", minH: 24, px: "$1.5", self: "center" }), _jsx(XStack, { flex: 1 }), _jsx(ChipSelect, { quiet: true, name: "Model", label: catalog.value.find((m) => m.id === kept.model)?.label ?? 'Enso', chosen: catalog.value.find((m) => m.id === kept.model) ?? null, items: catalog.value, onChange: (m) => set({ model: m.id }), placeholder: "Search models\u2026", placement: "top-end", loading: catalog.loading, error: catalog.error?.message ?? null }), _jsx(ChipSelect, { quiet: true, name: "Effort", label: EFFORTS.find((e) => e.id === kept.effort)?.label ?? 'Medium', chosen: EFFORTS.find((e) => e.id === kept.effort) ?? null, items: EFFORTS, onChange: (e) => set({ effort: e.id }), placement: "top-end", width: 180 })] }));
|
|
131
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, minW: 0, items: "center", px: "$4", children: [_jsxs(YStack, { width: "100%", maxW: COLUMN, flex: 1, minH: 0, children: [_jsx(EmptyPrompt, { title: "What\u2019s up next?", mark: _jsx(HanzoMark, { size: 18 }), column: COLUMN }), _jsx(YStack, { flex: 1 }), _jsxs(YStack, { pb: "$2", gap: "$2", children: [note || unhonoured(kept.mode) ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note || unhonoured(kept.mode) })) : null, _jsx(Composer, { inline: true, value: draft, onChange: setDraft, onSend: () => void send(), busy: busy, placeholder: "Describe a task or ask a question", label: "Describe a task or ask a question", head: head, foot: foot })] })] }), _jsx(Publish, { source: adding, onClose: () => setAdding(null) })] }));
|
|
131
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, minW: 0, items: "center", px: "$4", children: [_jsxs(YStack, { width: "100%", maxW: COLUMN, flex: 1, minH: 0, children: [_jsx(EmptyPrompt, { title: "What\u2019s up next?", mark: _jsx(HanzoMark, { size: 18 }), column: COLUMN }), _jsx(YStack, { flex: 1 }), _jsxs(YStack, { pb: "$2", gap: "$2", children: [note || unhonoured(kept.mode, place.id) ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note || unhonoured(kept.mode, place.id) })) : null, _jsx(Composer, { inline: true, value: draft, onChange: setDraft, onSend: () => void send(), busy: busy, placeholder: "Describe a task or ask a question", label: "Describe a task or ask a question", head: head, foot: foot })] })] }), _jsx(Publish, { source: adding, onClose: () => setAdding(null) })] }));
|
|
132
132
|
}
|
package/lib/project.js
CHANGED
|
@@ -239,7 +239,7 @@ export function Project({ slug }) {
|
|
|
239
239
|
}, empty: _jsxs(SizableText, { size: "$2", color: "$soft", p: "$4", children: ["Open a file from Files. Code here is read at ", ref, "; ask in the chat to change it."] }) })) : (_jsx(YStack, { p: "$4", gap: "$2", children: picked.length ? (picked.map((p) => (_jsxs(SizableText, { size: "$2", color: "$ink", children: [p.label, " \u2014 ", p.id] }, p.id)))) : (_jsx(SizableText, { size: "$2", color: "$soft", children: bridge
|
|
240
240
|
? 'Pick an element in the preview to see it here and attach it to your next ask.'
|
|
241
241
|
: 'Layers are read through the preview bridge, and this page does not run it. Its elements cannot be listed from here.' })) }));
|
|
242
|
-
return (_jsxs(_Fragment, { children: [_jsx(Workspace, { start: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(XStack, { render: "button", "aria-label": "All runs", onPress: () => host.go(''), p: "$1", children: _jsx(HanzoMark, { size: 20 }) }), _jsx(ProjectChip, { name: project?.name ?? slug, onPress: () => setSwitcher(true) }), _jsx(XStack, { render: "button", "aria-label": "History", onPress: () => setHistory(true), p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(Clock, { size: 16 }) }), _jsx(XStack, { render: "button", "aria-label": collapsed ? 'Show the chat' : 'Hide the chat', "aria-pressed": !collapsed, onPress: () => setCollapsed(!collapsed), p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(PanelLeft, { size: 16 }) })] }), middle: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Views, { views: VIEWS, value: view, onChange: (v) => { setView(v); setPane('view'); }, label: "View" }), _jsx(Views, { views: DEVICES, value: device, onChange: (d) => setDevice(d), label: "Device", labels: "none" }), _jsx(XStack, { render: "button", "aria-label": "Reload the preview", onPress: () => frame.current?.reload(), p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(RefreshCcw, { size: 16 }) }), _jsx(PageSelect, { pages: pages, value: page, onChange: setPage }), src ? (_jsx(Out, { href: src, label: "Open in a new tab", children: _jsx(XStack, { p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(ExternalLink, { size: 16 }) }) })) : null] }), end: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsxs(Button, { variant: "outline", size: "sm", onPress: () => void share(), children: [_jsx(Share2, { size: 14 }), " Share"] }), _jsxs(Button, { size: "sm", disabled: !project?.repo, onPress: () => project &&
|
|
242
|
+
return (_jsxs(_Fragment, { children: [_jsx(Workspace, { start: _jsxs(XStack, { items: "center", gap: "$2", minW: 0, shrink: 1, children: [_jsx(XStack, { render: "button", "aria-label": "All runs", onPress: () => host.go(''), p: "$1", shrink: 0, children: _jsx(HanzoMark, { size: 20 }) }), _jsx(ProjectChip, { name: project?.name ?? slug, onPress: () => setSwitcher(true) }), _jsx(XStack, { render: "button", "aria-label": "History", onPress: () => setHistory(true), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(Clock, { size: 16 }) }), _jsx(XStack, { render: "button", "aria-label": collapsed ? 'Show the chat' : 'Hide the chat', "aria-pressed": !collapsed, onPress: () => setCollapsed(!collapsed), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(PanelLeft, { size: 16 }) })] }), middle: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Views, { views: VIEWS, value: view, onChange: (v) => { setView(v); setPane('view'); }, label: "View" }), _jsx(Views, { views: DEVICES, value: device, onChange: (d) => setDevice(d), label: "Device", labels: "none" }), _jsx(XStack, { render: "button", "aria-label": "Reload the preview", onPress: () => frame.current?.reload(), p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(RefreshCcw, { size: 16 }) }), _jsx(PageSelect, { pages: pages, value: page, onChange: setPage }), src ? (_jsx(Out, { href: src, label: "Open in a new tab", children: _jsx(XStack, { p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(ExternalLink, { size: 16 }) }) })) : null] }), end: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(XStack, { display: "none", $md: { display: 'flex' }, children: _jsxs(Button, { variant: "outline", size: "sm", onPress: () => void share(), children: [_jsx(Share2, { size: 14 }), " Share"] }) }), _jsxs(Button, { size: "sm", disabled: !project?.repo, onPress: () => project &&
|
|
243
243
|
setPublish({ repo: project.repo, title: project.name, ref, name: project.slug, project: project.slug }), children: [_jsx(Upload, { size: 14 }), " Publish"] })] }), chat: chat, dock: _jsx(Console, { lines: lines, height: dock, onHeight: setDock, onClear: () => setPageLines([]), status: run.status || end.status || undefined, label: "Console" }), collapsed: collapsed, pane: pane, children: body }), _jsx(Dialog, { open: history, onOpenChange: setHistory, children: _jsxs(DialogContent, { maxW: 480, children: [_jsxs(DialogTitle, { children: ["Runs on ", project?.name ?? slug] }), _jsx(YStack, { gap: "$1", children: runs.value.length === 0 ? (_jsx(SizableText, { size: "$2", color: "$soft", children: "No runs yet." })) : (runs.value.map((r) => (_jsxs(XStack, { render: "button", onPress: () => {
|
|
244
244
|
setChosen(r.id);
|
|
245
245
|
setHistory(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/build",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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 page (hanzo.build) and as a component (hanzo.ai/dev).",
|