@hanzo/build 0.2.51 → 0.2.53

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.
Files changed (98) hide show
  1. package/README.md +109 -55
  2. package/lib/api/billing.d.ts +39 -0
  3. package/lib/api/billing.js +41 -0
  4. package/lib/api/coding.d.ts +4 -1
  5. package/lib/api/coding.js +2 -0
  6. package/lib/api/environment.d.ts +12 -0
  7. package/lib/api/environment.js +39 -1
  8. package/lib/api/github.d.ts +42 -4
  9. package/lib/api/github.js +79 -16
  10. package/lib/api/limits.d.ts +69 -0
  11. package/lib/api/limits.js +86 -0
  12. package/lib/api/links.d.ts +42 -0
  13. package/lib/api/links.js +65 -0
  14. package/lib/api/pref.d.ts +5 -3
  15. package/lib/api/pref.js +0 -3
  16. package/lib/api/turn.d.ts +10 -1
  17. package/lib/api/turn.js +16 -4
  18. package/lib/api/work.d.ts +75 -24
  19. package/lib/api/work.js +132 -27
  20. package/lib/builder.d.ts +0 -10
  21. package/lib/builder.js +21 -54
  22. package/lib/choice.d.ts +21 -0
  23. package/lib/choice.js +40 -1
  24. package/lib/customize/agents.d.ts +1 -1
  25. package/lib/customize/agents.js +21 -19
  26. package/lib/customize/connectors.d.ts +1 -1
  27. package/lib/customize/connectors.js +80 -31
  28. package/lib/customize/index.d.ts +2 -0
  29. package/lib/customize/index.js +23 -19
  30. package/lib/customize/load.d.ts +9 -0
  31. package/lib/customize/load.js +22 -0
  32. package/lib/customize/plugins.d.ts +1 -1
  33. package/lib/customize/plugins.js +21 -19
  34. package/lib/customize/say.d.ts +1 -0
  35. package/lib/customize/say.js +38 -0
  36. package/lib/customize/skills.d.ts +1 -1
  37. package/lib/customize/skills.js +23 -17
  38. package/lib/customize/ui.d.ts +28 -11
  39. package/lib/customize/ui.js +29 -21
  40. package/lib/data.d.ts +13 -0
  41. package/lib/data.js +70 -0
  42. package/lib/desk.d.ts +6 -1
  43. package/lib/desk.js +43 -19
  44. package/lib/environment.d.ts +11 -3
  45. package/lib/environment.js +53 -13
  46. package/lib/foot.js +10 -6
  47. package/lib/forge.js +255 -92
  48. package/lib/host.d.ts +1 -1
  49. package/lib/index.d.ts +28 -2
  50. package/lib/index.js +26 -2
  51. package/lib/landing.d.ts +0 -10
  52. package/lib/landing.js +79 -47
  53. package/lib/merge.d.ts +223 -0
  54. package/lib/merge.js +380 -0
  55. package/lib/meter.d.ts +31 -0
  56. package/lib/meter.js +45 -0
  57. package/lib/mind.d.ts +60 -0
  58. package/lib/mind.js +187 -0
  59. package/lib/nav.d.ts +11 -0
  60. package/lib/nav.js +75 -0
  61. package/lib/panel.d.ts +45 -0
  62. package/lib/panel.js +115 -0
  63. package/lib/pick.d.ts +8 -0
  64. package/lib/pick.js +18 -1
  65. package/lib/plan.d.ts +78 -0
  66. package/lib/plan.js +133 -0
  67. package/lib/project.d.ts +16 -1
  68. package/lib/project.js +100 -32
  69. package/lib/prompt.d.ts +75 -0
  70. package/lib/prompt.js +125 -0
  71. package/lib/prose.js +14 -10
  72. package/lib/reply.d.ts +36 -0
  73. package/lib/reply.js +124 -0
  74. package/lib/route.d.ts +28 -4
  75. package/lib/route.js +48 -1
  76. package/lib/run.js +87 -52
  77. package/lib/section.d.ts +1 -1
  78. package/lib/section.js +7 -11
  79. package/lib/settings/billing.js +16 -9
  80. package/lib/settings/code.js +15 -12
  81. package/lib/settings/credits.d.ts +9 -0
  82. package/lib/settings/credits.js +110 -0
  83. package/lib/settings/plan.d.ts +17 -0
  84. package/lib/settings/plan.js +108 -0
  85. package/lib/settings/usage.js +11 -119
  86. package/lib/shelf.js +3 -3
  87. package/lib/standing.d.ts +44 -0
  88. package/lib/standing.js +64 -0
  89. package/lib/sync.js +23 -9
  90. package/lib/tabs.d.ts +53 -0
  91. package/lib/tabs.js +197 -0
  92. package/lib/tools.d.ts +2 -9
  93. package/lib/tools.js +20 -25
  94. package/lib/transcript.d.ts +10 -1
  95. package/lib/transcript.js +25 -10
  96. package/lib/voice.d.ts +2 -13
  97. package/lib/voice.js +7 -73
  98. package/package.json +20 -3
@@ -13,8 +13,9 @@ import { Plus, X } from '@hanzogui/lucide-icons-2';
13
13
  import { Button, Dialog, DialogContent, DialogTitle, Input, Textarea } from '@hanzo/ui';
14
14
  import { useEffect, useState } from 'react';
15
15
  import { start } from './api/coding.js';
16
- import { read, refuse, removeSecret, save, setSecret, SETUP } from './api/environment.js';
16
+ import { plain, read, refuse, removeSecret, save, setSecret, SETUP } from './api/environment.js';
17
17
  import { useRead } from './data.js';
18
+ import { matches } from './merge.js';
18
19
  import { useHost, useTarget } from './host.js';
19
20
  const mono = { fontFamily: 'var(--f-mono, ui-monospace, monospace)' };
20
21
  export function Environment({ repo, busy }) {
@@ -44,7 +45,7 @@ export function Environment({ repo, busy }) {
44
45
  return _jsx(Soft, { children: "This run has no codebase, so it has no environment." });
45
46
  }
46
47
  if (env.error) {
47
- return _jsx(Soft, { children: env.error.message });
48
+ return _jsx(Soft, { children: `${repo}’s environment could not be read right now.` });
48
49
  }
49
50
  if (!shown) {
50
51
  return _jsx(Soft, { children: "Reading the environment\u2026" });
@@ -63,7 +64,7 @@ export function Environment({ repo, busy }) {
63
64
  return true;
64
65
  }
65
66
  catch (e) {
66
- setNote(e.message);
67
+ setNote(plain(e, repo, 'save'));
67
68
  return false;
68
69
  }
69
70
  finally {
@@ -78,7 +79,7 @@ export function Environment({ repo, busy }) {
78
79
  host.go(run.session);
79
80
  }
80
81
  catch (e) {
81
- setNote(e.message);
82
+ setNote(plain(e, repo, 'start'));
82
83
  setWorking(false);
83
84
  }
84
85
  };
@@ -102,27 +103,66 @@ export function Environment({ repo, busy }) {
102
103
  /**
103
104
  * New's offer to set a codebase up. An agent onboards it, or an org admin saves
104
105
  * it empty and writes the scripts by hand beside the next run.
106
+ *
107
+ * The repository is chosen from the org's own (the forge's, and every linked
108
+ * repository's copy there), never typed: a name that is not one of them — a
109
+ * board's key handed over as a codebase — is said so and cannot be set up.
105
110
  */
106
- export function SetupDialog({ repo, open, onOpenChange, onStart, onSaved, }) {
111
+ export function SetupDialog({ repo, homes, open, onOpenChange, onStart, onSaved, onRepo, }) {
107
112
  const host = useHost();
108
113
  const t = useTarget();
109
- const [working, setWorking] = useState(false);
114
+ const [chosen, setChosen] = useState(repo);
115
+ const [q, setQ] = useState('');
116
+ const [working, setWorking] = useState('');
110
117
  const [note, setNote] = useState('');
111
- const run = async (what, done) => {
112
- setWorking(true);
118
+ const [last, setLast] = useState('');
119
+ useEffect(() => {
120
+ if (!open)
121
+ return;
122
+ setChosen(repo);
123
+ setQ('');
124
+ setNote('');
125
+ setLast('');
126
+ }, [open, repo]);
127
+ const list = homes.value.homes;
128
+ const home = list.find((h) => h.name.toLowerCase() === chosen.toLowerCase()) ?? null;
129
+ // A name the whole list does not hold is not the org's; with part of the list unread, the platform decides.
130
+ const stranger = Boolean(chosen) && !home && homes.value.whole && !homes.loading;
131
+ const ready = Boolean(chosen) && !stranger && !homes.loading;
132
+ const shown = list.filter((h) => matches(q, h.label, h.name)).slice(0, 6);
133
+ const run = async (what) => {
134
+ if (!ready)
135
+ return;
136
+ // The org's own spelling of the name, when the list holds it.
137
+ const repo = home?.name ?? chosen;
138
+ setWorking(what);
139
+ setLast(what);
113
140
  setNote('');
114
141
  try {
115
- await what();
116
- done();
142
+ if (what === 'save') {
143
+ await save(t, repo, { install: '', start: '' });
144
+ onSaved(repo);
145
+ }
146
+ else {
147
+ await onStart(repo);
148
+ onOpenChange(false);
149
+ }
117
150
  }
118
151
  catch (e) {
119
- setNote(e.message);
152
+ setNote(plain(e, repo, what));
120
153
  }
121
154
  finally {
122
- setWorking(false);
155
+ setWorking('');
123
156
  }
124
157
  };
125
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { maxW: 440, showCloseButton: false, children: [_jsxs(XStack, { items: "center", justify: "space-between", gap: "$2", children: [_jsx(DialogTitle, { children: "Set up an environment" }), _jsx(XStack, { render: "button", "aria-label": "Close", p: "$1", onPress: () => onOpenChange(false), children: _jsx(X, { size: 16 }) })] }), _jsxs(YStack, { gap: "$3", children: [_jsxs(YStack, { gap: "$1.5", children: [_jsx(SizableText, { size: "$2", color: "$soft", children: "Repository" }), _jsx(XStack, { px: "$3", py: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: _jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, children: repo }) })] }), _jsx(SizableText, { size: "$2", color: "$soft", children: "An agent onboards the codebase: it explores it, writes the install and start scripts, and names the secrets it needs. It takes several minutes. Interrupt it anytime, or take over in the terminal." }), note ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note })) : null, _jsxs(XStack, { gap: "$2", justify: "space-between", items: "center", children: [host.admin ? (_jsx(Button, { size: "sm", variant: "secondary", disabled: working, onPress: () => void run(() => save(t, repo, { install: '', start: '' }), onSaved), children: "Skip & save" })) : (_jsx(YStack, {})), _jsx(Button, { size: "sm", variant: "primary", disabled: working, onPress: () => void run(onStart, () => onOpenChange(false)), children: "Start agent" })] })] })] }) }));
158
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { maxW: 480, showCloseButton: false, children: [_jsxs(XStack, { items: "center", justify: "space-between", gap: "$2", children: [_jsx(DialogTitle, { children: "Set up an environment" }), _jsx(XStack, { render: "button", "aria-label": "Close", p: "$1", onPress: () => onOpenChange(false), children: _jsx(X, { size: 16 }) })] }), _jsxs(YStack, { gap: "$3", children: [_jsxs(YStack, { gap: "$1.5", children: [_jsx(SizableText, { size: "$2", color: "$soft", children: "Repository" }), _jsx(XStack, { px: "$3", py: "$2", rounded: "$3", borderWidth: 1, borderColor: stranger ? '$ink' : '$borderColor', items: "center", gap: "$2", "aria-label": "Chosen repository", children: _jsx(SizableText, { size: "$2", color: home || (chosen && !stranger) ? '$ink' : '$soft', numberOfLines: 1, flex: 1, minW: 0, children: home ? home.label : chosen || 'Choose a repository' }) }), stranger ? (_jsx(SizableText, { size: "$1", color: "$ink", role: "alert", children: `${chosen} is not one of this organization’s repositories. Choose one below.` })) : null, _jsx(Input, { value: q, onChangeText: setQ, placeholder: "Find a repository\u2026", "aria-label": "Find a repository" }), _jsx(YStack, { gap: 2, "aria-label": "Repositories", children: homes.loading && !list.length ? (_jsx(SizableText, { size: "$1", color: "$soft", children: "Reading the repositories\u2026" })) : homes.error && !list.length ? (_jsxs(XStack, { items: "center", gap: "$2", flexWrap: "wrap", children: [_jsx(SizableText, { size: "$1", color: "$soft", children: "The repository list could not be read right now." }), _jsx(Button, { size: "sm", variant: "outline", onPress: homes.reload, children: "Read again" })] })) : shown.length === 0 ? (_jsx(SizableText, { size: "$1", color: "$soft", children: list.length ? 'Nothing matches.' : 'This organization has no repositories on the forge yet.' })) : (shown.map((h) => {
159
+ const on = h.name.toLowerCase() === chosen.toLowerCase();
160
+ return (_jsx(XStack, { render: "button", "aria-pressed": on, "aria-label": `Choose ${h.label}`, onPress: () => {
161
+ setChosen(h.name);
162
+ setNote('');
163
+ onRepo(h);
164
+ }, px: "$2.5", py: "$1.5", rounded: "$2", bg: on ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, children: _jsx(SizableText, { size: "$2", color: on ? '$ink' : '$soft', numberOfLines: 1, children: h.label }) }, h.name));
165
+ })) })] }), _jsx(SizableText, { size: "$2", color: "$soft", children: "An agent onboards the codebase: it explores it, writes the install and start scripts, and names the secrets it needs. It takes several minutes. Interrupt it anytime, or take over in the terminal." }), note ? (_jsxs(XStack, { items: "center", gap: "$2", flexWrap: "wrap", children: [_jsx(SizableText, { size: "$1", color: "$ink", role: "status", flex: 1, minW: 200, children: note }), last ? (_jsx(Button, { size: "sm", variant: "outline", disabled: working !== '', onPress: () => void run(last), children: "Try again" })) : null] })) : null, _jsxs(XStack, { gap: "$2", justify: "space-between", items: "center", children: [host.admin ? (_jsx(Button, { size: "sm", variant: "secondary", disabled: working !== '' || !ready, onPress: () => void run('save'), children: working === 'save' ? 'Saving…' : 'Skip & save' })) : (_jsx(YStack, {})), _jsx(Button, { size: "sm", variant: "primary", disabled: working !== '' || !ready, onPress: () => void run('start'), children: working === 'start' ? 'Starting the agent…' : 'Start agent' })] })] })] }) }));
126
166
  }
127
167
  function Script({ label, value, onChange, locked, hint }) {
128
168
  return (_jsxs(YStack, { gap: "$2", children: [_jsx(SizableText, { size: "$2", color: "$ink", children: label }), _jsx(Textarea, { value: value, onChangeText: onChange, disabled: locked, placeholder: hint, "aria-label": label, rows: 3, style: mono })] }));
package/lib/foot.js CHANGED
@@ -1,16 +1,19 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * The rail's foot: the card that offers Hanzo in Slack, and the menu the account
4
- * row opens — who is signed in, the organization they act in, Settings, Usage,
5
- * the plans, help and logging out.
4
+ * row opens — who is signed in, the plan and its meter (`Meter`), the
5
+ * organization they act in, Settings, Usage, Billing, the plans, help and
6
+ * logging out.
6
7
  * Everything it opens is an address of this page, but help, which is the
7
8
  * documentation.
8
9
  */
9
10
  import { useCallback, useEffect, useRef, useState } from 'react';
10
11
  import { Popover, SizableText, XStack, YStack } from '@hanzo/gui';
11
- import { Gauge, LifeBuoy, LogOut, Settings, Slack as Mark, Sparkles, X } from '@hanzogui/lucide-icons-2';
12
+ import { CreditCard, Gauge, LifeBuoy, LogOut, Settings, Slack as Mark, Sparkles, X } from '@hanzogui/lucide-icons-2';
12
13
  import { MenuLabel, MenuRow, MenuRule } from '@hanzo/ui/product';
13
- import { useHost } from './host.js';
14
+ import { useHost, useTarget } from './host.js';
15
+ import { Meter } from './meter.js';
16
+ import { useStanding } from './standing.js';
14
17
  import { path } from './route.js';
15
18
  export const DOCS = 'https://docs.hanzo.ai/docs/dev';
16
19
  /** Hanzo in Slack is set up under Settings; the card goes there, and once dismissed stays gone. */
@@ -52,17 +55,18 @@ export function useWho() {
52
55
  */
53
56
  export function Who({ open, onOpenChange, narrow }) {
54
57
  const host = useHost();
58
+ const standing = useStanding(useTarget(), open);
55
59
  // The organizations this person acts in; choosing one reloads the page scoped to it.
56
60
  const orgs = host.memberships ?? (host.org ? [host.org] : []);
57
61
  const go = (p) => {
58
62
  onOpenChange(false);
59
63
  host.go(p);
60
64
  };
61
- return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 6, children: [_jsx(Popover.Anchor, { position: "absolute", l: 8, b: 8, width: narrow ? 48 : 256, height: 36, pointerEvents: "none" }), _jsx(Popover.Content, { role: "menu", "aria-label": "Account", items: "stretch", borderWidth: 1, elevation: "$2", px: "$1", py: "$1", width: 260, bg: "$panel", borderColor: "$borderColor", children: _jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person?.email || host.person?.name || '' }), orgs.length ? (_jsxs(_Fragment, { children: [_jsx(MenuRule, {}), _jsx(MenuLabel, { children: "Organization" }), _jsx(YStack, { role: "radiogroup", "aria-label": "Organizations", gap: "$1", children: orgs.map((o) => (_jsx(MenuRow, { label: o, active: o === host.org, onPress: () => {
65
+ return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 6, children: [_jsx(Popover.Anchor, { position: "absolute", l: 8, b: 8, width: narrow ? 48 : 256, height: 36, pointerEvents: "none" }), _jsx(Popover.Content, { role: "menu", "aria-label": "Account", items: "stretch", borderWidth: 1, elevation: "$2", px: "$1", py: "$1", width: 260, bg: "$panel", borderColor: "$borderColor", children: _jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person?.email || host.person?.name || '' }), _jsx(MenuRule, {}), _jsx(Meter, { read: standing, onPlans: () => go(path({ kind: 'screen', screen: 'plans' })), onBilling: () => go(path({ kind: 'settings', section: 'billing' })) }), orgs.length ? (_jsxs(_Fragment, { children: [_jsx(MenuRule, {}), _jsx(MenuLabel, { children: "Organization" }), _jsx(YStack, { role: "radiogroup", "aria-label": "Organizations", gap: "$1", children: orgs.map((o) => (_jsx(MenuRow, { label: o, active: o === host.org, onPress: () => {
62
66
  onOpenChange(false);
63
67
  if (o !== host.org)
64
68
  host.chooseOrg?.(o);
65
- } }, o))) })] })) : null, _jsx(MenuRule, {}), _jsx(MenuRow, { label: "Settings", icon: _jsx(Settings, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(MenuRow, { label: "Usage", icon: _jsx(Gauge, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(MenuRow, { label: "View all plans", icon: _jsx(Sparkles, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'plans' })) }), _jsx(MenuRow, { label: "Get help", icon: _jsx(LifeBuoy, { size: 16 }), onPress: () => {
69
+ } }, o))) })] })) : null, _jsx(MenuRule, {}), _jsx(MenuRow, { label: "Settings", icon: _jsx(Settings, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(MenuRow, { label: "Usage", icon: _jsx(Gauge, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(MenuRow, { label: "Billing", icon: _jsx(CreditCard, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'billing' })) }), _jsx(MenuRow, { label: "View all plans", icon: _jsx(Sparkles, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'plans' })) }), _jsx(MenuRow, { label: "Get help", icon: _jsx(LifeBuoy, { size: 16 }), onPress: () => {
66
70
  onOpenChange(false);
67
71
  window.open(DOCS, '_blank', 'noopener,noreferrer');
68
72
  } }), host.signOut ? (_jsxs(_Fragment, { children: [_jsx(MenuRule, {}), _jsx(MenuRow, { label: "Log out", icon: _jsx(LogOut, { size: 16 }), onPress: () => {