@hanzo/rooms 0.1.0

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 (235) hide show
  1. package/LICENSE-APACHE +205 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +23 -0
  4. package/dist/Account.d.ts +8 -0
  5. package/dist/Account.js +58 -0
  6. package/dist/Beluga.d.ts +12 -0
  7. package/dist/Beluga.js +152 -0
  8. package/dist/Billing.d.ts +1 -0
  9. package/dist/Billing.js +31 -0
  10. package/dist/Board.d.ts +9 -0
  11. package/dist/Board.js +447 -0
  12. package/dist/Boxes.d.ts +1 -0
  13. package/dist/Boxes.js +173 -0
  14. package/dist/CalScheduler.d.ts +15 -0
  15. package/dist/CalScheduler.js +196 -0
  16. package/dist/Chat.d.ts +35 -0
  17. package/dist/Chat.js +1049 -0
  18. package/dist/Column.d.ts +15 -0
  19. package/dist/Column.js +88 -0
  20. package/dist/Contacts.d.ts +1 -0
  21. package/dist/Contacts.js +301 -0
  22. package/dist/Directory.d.ts +24 -0
  23. package/dist/Directory.js +356 -0
  24. package/dist/Drive.d.ts +2 -0
  25. package/dist/Drive.js +466 -0
  26. package/dist/Find.d.ts +7 -0
  27. package/dist/Find.js +82 -0
  28. package/dist/Guide.d.ts +6 -0
  29. package/dist/Guide.js +272 -0
  30. package/dist/Home.d.ts +39 -0
  31. package/dist/Home.js +306 -0
  32. package/dist/Inbox.d.ts +9 -0
  33. package/dist/Inbox.js +193 -0
  34. package/dist/Market.d.ts +3 -0
  35. package/dist/Market.js +149 -0
  36. package/dist/Me.d.ts +27 -0
  37. package/dist/Me.js +251 -0
  38. package/dist/Meet.d.ts +9 -0
  39. package/dist/Meet.js +370 -0
  40. package/dist/ModeMark.d.ts +8 -0
  41. package/dist/ModeMark.js +18 -0
  42. package/dist/OrgSwitcher.d.ts +31 -0
  43. package/dist/OrgSwitcher.js +120 -0
  44. package/dist/Orgs.d.ts +10 -0
  45. package/dist/Orgs.js +582 -0
  46. package/dist/Profile.d.ts +4 -0
  47. package/dist/Profile.js +67 -0
  48. package/dist/Prose.d.ts +5 -0
  49. package/dist/Prose.js +111 -0
  50. package/dist/ProviderMark.d.ts +17 -0
  51. package/dist/ProviderMark.js +277 -0
  52. package/dist/Recents.d.ts +10 -0
  53. package/dist/Recents.js +91 -0
  54. package/dist/RightPane.d.ts +13 -0
  55. package/dist/RightPane.js +426 -0
  56. package/dist/Room.d.ts +8 -0
  57. package/dist/Room.js +50 -0
  58. package/dist/Scene.d.ts +12 -0
  59. package/dist/Scene.js +105 -0
  60. package/dist/Settings.d.ts +46 -0
  61. package/dist/Settings.js +321 -0
  62. package/dist/Setup.d.ts +1 -0
  63. package/dist/Setup.js +36 -0
  64. package/dist/Share.d.ts +3 -0
  65. package/dist/Share.js +130 -0
  66. package/dist/Shared.d.ts +1 -0
  67. package/dist/Shared.js +113 -0
  68. package/dist/Shell.d.ts +63 -0
  69. package/dist/Shell.js +1225 -0
  70. package/dist/Start.d.ts +1 -0
  71. package/dist/Start.js +96 -0
  72. package/dist/Support.d.ts +14 -0
  73. package/dist/Support.js +122 -0
  74. package/dist/Talk.d.ts +4 -0
  75. package/dist/Talk.js +107 -0
  76. package/dist/Upgrade.d.ts +11 -0
  77. package/dist/Upgrade.js +75 -0
  78. package/dist/Usage.d.ts +1 -0
  79. package/dist/Usage.js +21 -0
  80. package/dist/Vibe.d.ts +2 -0
  81. package/dist/Vibe.js +188 -0
  82. package/dist/Work.d.ts +12 -0
  83. package/dist/Work.js +256 -0
  84. package/dist/bots/Bots.d.ts +2 -0
  85. package/dist/bots/Bots.js +456 -0
  86. package/dist/bots/Lights.d.ts +8 -0
  87. package/dist/bots/Lights.js +13 -0
  88. package/dist/bots/api.d.ts +120 -0
  89. package/dist/bots/api.js +75 -0
  90. package/dist/bots/face.d.ts +5 -0
  91. package/dist/bots/face.js +13 -0
  92. package/dist/bots/ink.d.ts +7 -0
  93. package/dist/bots/ink.js +29 -0
  94. package/dist/bots/kit.d.ts +113 -0
  95. package/dist/bots/kit.js +43 -0
  96. package/dist/bots/runtime.d.ts +4 -0
  97. package/dist/bots/runtime.js +326 -0
  98. package/dist/card.d.ts +52 -0
  99. package/dist/card.js +52 -0
  100. package/dist/cast.d.ts +143 -0
  101. package/dist/cast.js +251 -0
  102. package/dist/connect.d.ts +29 -0
  103. package/dist/connect.js +64 -0
  104. package/dist/contacts-store.d.ts +111 -0
  105. package/dist/contacts-store.js +276 -0
  106. package/dist/conversations.d.ts +54 -0
  107. package/dist/conversations.js +98 -0
  108. package/dist/copy.d.ts +23 -0
  109. package/dist/copy.js +39 -0
  110. package/dist/crew.d.ts +36 -0
  111. package/dist/crew.js +304 -0
  112. package/dist/dev.d.ts +3 -0
  113. package/dist/dev.js +102 -0
  114. package/dist/enso.d.ts +70 -0
  115. package/dist/enso.js +112 -0
  116. package/dist/failure.d.ts +57 -0
  117. package/dist/failure.js +143 -0
  118. package/dist/ground.d.ts +32 -0
  119. package/dist/ground.js +32 -0
  120. package/dist/host.d.ts +76 -0
  121. package/dist/host.js +76 -0
  122. package/dist/index.d.ts +14 -0
  123. package/dist/index.js +12 -0
  124. package/dist/kept.d.ts +5 -0
  125. package/dist/kept.js +27 -0
  126. package/dist/lib/account.d.ts +49 -0
  127. package/dist/lib/account.js +229 -0
  128. package/dist/lib/ai.d.ts +46 -0
  129. package/dist/lib/ai.js +140 -0
  130. package/dist/lib/api.d.ts +43 -0
  131. package/dist/lib/api.js +62 -0
  132. package/dist/lib/bare.d.ts +9 -0
  133. package/dist/lib/bare.js +7 -0
  134. package/dist/lib/coding.d.ts +105 -0
  135. package/dist/lib/coding.js +87 -0
  136. package/dist/lib/destination.d.ts +102 -0
  137. package/dist/lib/destination.js +260 -0
  138. package/dist/lib/durable.d.ts +66 -0
  139. package/dist/lib/durable.js +107 -0
  140. package/dist/lib/first.d.ts +11 -0
  141. package/dist/lib/first.js +23 -0
  142. package/dist/lib/home.d.ts +61 -0
  143. package/dist/lib/home.js +54 -0
  144. package/dist/lib/host.d.ts +104 -0
  145. package/dist/lib/host.js +132 -0
  146. package/dist/lib/hostname.d.ts +1 -0
  147. package/dist/lib/hostname.js +14 -0
  148. package/dist/lib/hydrated.d.ts +1 -0
  149. package/dist/lib/hydrated.js +20 -0
  150. package/dist/lib/integrations.d.ts +21 -0
  151. package/dist/lib/integrations.js +1 -0
  152. package/dist/lib/intent.d.ts +6 -0
  153. package/dist/lib/intent.js +71 -0
  154. package/dist/lib/invite.d.ts +155 -0
  155. package/dist/lib/invite.js +245 -0
  156. package/dist/lib/limits.d.ts +55 -0
  157. package/dist/lib/limits.js +119 -0
  158. package/dist/lib/list.d.ts +9 -0
  159. package/dist/lib/list.js +11 -0
  160. package/dist/lib/mark.d.ts +13 -0
  161. package/dist/lib/mark.js +85 -0
  162. package/dist/lib/mix.d.ts +22 -0
  163. package/dist/lib/mix.js +22 -0
  164. package/dist/lib/models.d.ts +27 -0
  165. package/dist/lib/models.js +106 -0
  166. package/dist/lib/org.d.ts +109 -0
  167. package/dist/lib/org.js +140 -0
  168. package/dist/lib/path.d.ts +9 -0
  169. package/dist/lib/path.js +29 -0
  170. package/dist/lib/plans.d.ts +193 -0
  171. package/dist/lib/plans.js +310 -0
  172. package/dist/lib/prose.d.ts +13 -0
  173. package/dist/lib/prose.js +62 -0
  174. package/dist/lib/reach.d.ts +17 -0
  175. package/dist/lib/reach.js +70 -0
  176. package/dist/lib/rooms.json +23 -0
  177. package/dist/lib/served.d.ts +20 -0
  178. package/dist/lib/served.js +92 -0
  179. package/dist/lib/session.d.ts +211 -0
  180. package/dist/lib/session.js +493 -0
  181. package/dist/lib/share.d.ts +131 -0
  182. package/dist/lib/share.js +216 -0
  183. package/dist/lib/signout.d.ts +14 -0
  184. package/dist/lib/signout.js +40 -0
  185. package/dist/lib/tags.d.ts +27 -0
  186. package/dist/lib/tags.js +46 -0
  187. package/dist/lib/tap.d.ts +17 -0
  188. package/dist/lib/tap.js +9 -0
  189. package/dist/lib/tier.d.ts +115 -0
  190. package/dist/lib/tier.js +207 -0
  191. package/dist/lib/tiers.d.ts +9 -0
  192. package/dist/lib/tiers.js +141 -0
  193. package/dist/lib/todo.d.ts +125 -0
  194. package/dist/lib/todo.js +379 -0
  195. package/dist/look.d.ts +1 -0
  196. package/dist/look.js +21 -0
  197. package/dist/meters.d.ts +8 -0
  198. package/dist/meters.js +31 -0
  199. package/dist/model.d.ts +4 -0
  200. package/dist/model.js +44 -0
  201. package/dist/network.d.ts +64 -0
  202. package/dist/network.js +216 -0
  203. package/dist/open.d.ts +81 -0
  204. package/dist/open.js +149 -0
  205. package/dist/pane.d.ts +92 -0
  206. package/dist/pane.js +231 -0
  207. package/dist/progress.d.ts +46 -0
  208. package/dist/progress.js +51 -0
  209. package/dist/recent.d.ts +67 -0
  210. package/dist/recent.js +161 -0
  211. package/dist/rooms.d.ts +131 -0
  212. package/dist/rooms.js +195 -0
  213. package/dist/roster.d.ts +28 -0
  214. package/dist/roster.js +70 -0
  215. package/dist/span.d.ts +14 -0
  216. package/dist/span.js +50 -0
  217. package/dist/speech.d.ts +44 -0
  218. package/dist/speech.js +229 -0
  219. package/dist/stars.d.ts +16 -0
  220. package/dist/stars.js +75 -0
  221. package/dist/tabs.d.ts +26 -0
  222. package/dist/tabs.js +25 -0
  223. package/dist/team.d.ts +11 -0
  224. package/dist/team.gen.d.ts +10 -0
  225. package/dist/team.gen.js +11 -0
  226. package/dist/team.js +30 -0
  227. package/dist/tools.d.ts +35 -0
  228. package/dist/tools.js +143 -0
  229. package/dist/tours.d.ts +24 -0
  230. package/dist/tours.js +311 -0
  231. package/dist/waiting.d.ts +15 -0
  232. package/dist/waiting.js +17 -0
  233. package/dist/where.d.ts +54 -0
  234. package/dist/where.js +14 -0
  235. package/package.json +108 -0
package/dist/Boxes.js ADDED
@@ -0,0 +1,173 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // The compute an org is HOLDING: every sandbox lease, what it costs by the
4
+ // hour, and the two things you can do to one.
5
+ //
6
+ // A LEASE IS MONEY RUNNING. It is billed per hour of being held, at the price of
7
+ // the envelope it holds — so the rate is on the row rather than in a pricing page
8
+ // nobody has open, and the total is stated before anything is taken. A surface
9
+ // that hides the meter is how a $0.96 hour becomes a surprise.
10
+ //
11
+ // RESUMING IS LEASING WITH AN ID. There is no separate verb: `lease({ id })`
12
+ // answers the same sandbox if it is still there and a NEW one if the lease
13
+ // expired while you were reading, which is the honest answer to resuming
14
+ // something that is gone. So the button says Resume and the id it returns is
15
+ // what a later call must use.
16
+ //
17
+ // ENDING IS NOT STOPPING. `stop` interrupts what is running and KEEPS the lease
18
+ // — the meter keeps going. `end` releases it, which is what stops the charge.
19
+ // Both are drawn, because a runaway command and an unwanted box are different
20
+ // problems and one control for both would do the wrong one half the time.
21
+ import { useCallback, useEffect, useState } from 'react';
22
+ import { say } from './failure.js';
23
+ import { Play, Plus, Square, X } from 'lucide-react';
24
+ import { ChannelHeader } from '@hanzo/ui/agents';
25
+ import { Box, Text, XStack, YStack } from '@hanzo/ui';
26
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@hanzo/ui';
27
+ import { useIam } from '@hanzo/iam/react';
28
+ import { hasSession } from './lib/session.js';
29
+ import { useAi } from './lib/ai.js';
30
+ /**
31
+ * The LIST price of an hour of each class, in micro-USD.
32
+ *
33
+ * The platform is the authority — it prices `agents/runtime-hour-<class>` and an
34
+ * operator retunes it there — and the surface cannot read it: the rate rows live
35
+ * behind `GET /v1/commerce/rates/entries`, which is SuperAdmin only and answers
36
+ * 403 to the reader holding this lease. Nothing else in the contract publishes a
37
+ * rate; a sandbox row carries none either.
38
+ *
39
+ * So the table stays — a control that spends money without naming the price is
40
+ * the defect this file exists to avoid — and both places that draw it say LIST,
41
+ * because a number nobody asked the platform for must not read as a live quote.
42
+ * If the two ever disagree, the invoice is right and this is stale.
43
+ */
44
+ const HOURLY = {
45
+ exec: 80_000,
46
+ dev: 80_000,
47
+ desktop: 80_000,
48
+ // Twelve times the memory of the default envelope, and a node packs sixteen
49
+ // of those — so an android lease displaces twelve and is charged for twelve.
50
+ android: 960_000,
51
+ };
52
+ /** Micro-USD as money. Two places, because that is how an hourly rate reads. */
53
+ const usd = (micros) => `$${(micros / 1_000_000).toFixed(2)}`;
54
+ /** Unix seconds to how long is left, or the fact that it is over. */
55
+ const remaining = (expiresAt) => {
56
+ if (!expiresAt)
57
+ return '';
58
+ const left = expiresAt - Math.floor(Date.now() / 1000);
59
+ if (left <= 0)
60
+ return 'expired';
61
+ if (left < 3600)
62
+ return `${Math.ceil(left / 60)}m left`;
63
+ return `${Math.floor(left / 3600)}h ${Math.ceil((left % 3600) / 60)}m left`;
64
+ };
65
+ /**
66
+ * Take a new lease.
67
+ *
68
+ * THE PROJECT IS THE DISK, and that is the whole reason this control asks for
69
+ * one. A volume is named per (org, project) and the name is deterministic, so
70
+ * leasing the same project again finds the SAME disk without a lookup — which is
71
+ * how a conversation continues across a lease that ended. `exec` keeps nothing
72
+ * and takes no project; every other class requires one, so the field appears
73
+ * exactly where it decides something.
74
+ *
75
+ * The rate is stated on the button because taking this lease starts a meter.
76
+ */
77
+ function Start({ onDone }) {
78
+ const { client } = useAi();
79
+ const [klass, setKlass] = useState('exec');
80
+ const [project, setProject] = useState('');
81
+ const [busy, setBusy] = useState(false);
82
+ const [failed, setFailed] = useState(null);
83
+ const keeps = klass !== 'exec';
84
+ const take = useCallback(async () => {
85
+ if (!client || busy)
86
+ return;
87
+ if (keeps && !project.trim()) {
88
+ setFailed(`A ${klass} sandbox keeps a disk, so it needs a project to name it.`);
89
+ return;
90
+ }
91
+ setBusy(true);
92
+ setFailed(null);
93
+ try {
94
+ await client.sandboxes.lease({
95
+ class: klass,
96
+ ...(keeps ? { project: project.trim() } : {}),
97
+ });
98
+ onDone();
99
+ }
100
+ catch (e) {
101
+ setFailed(e.message);
102
+ }
103
+ finally {
104
+ setBusy(false);
105
+ }
106
+ }, [client, busy, keeps, klass, project, onDone]);
107
+ return (_jsxs(YStack, { gap: "$2", p: "$4", borderBottomWidth: 1, borderColor: "$borderColor", children: [_jsxs(XStack, { gap: "$2", items: "center", flexWrap: "wrap", children: [_jsxs(Select, { value: klass, onValueChange: (v) => setKlass(v), children: [_jsx(SelectTrigger, { "aria-label": "Sandbox class", width: 200, children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: Object.keys(HOURLY).map((c) => (_jsxs(SelectItem, { value: c, children: [c, " \u2014 ", usd(HOURLY[c]), "/hr list"] }, c))) })] }), keeps ? (_jsx(XStack, { flex: 1, minW: 160, height: 34, items: "center", px: "$3", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", children: _jsx(Text, { render: _jsx("input", { value: project, onChange: (e) => setProject(e.target.value), onKeyDown: (e) => e.key === 'Enter' && void take(), placeholder: "Project \u2014 the disk it keeps" }), flex: 1, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit" }) })) : (_jsx(Text, { fontSize: "$1", color: "$soft", flex: 1, children: "Keeps nothing \u2014 an exec sandbox has no disk." })), _jsx(Box, { render: "button", onClick: () => void take(), px: "$3", py: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Plus, { size: 13, "aria-hidden": true }), _jsx(Text, { fontSize: "$2", color: "$ink", children: busy ? 'Leasing' : 'Lease' })] }) })] }), failed ? (_jsx(Text, { fontSize: "$1", color: "$soft", numberOfLines: 2, children: failed })) : null] }));
108
+ }
109
+ function Empty({ children }) {
110
+ return (_jsx(YStack, { flex: 1, items: "center", justify: "center", p: "$6", children: _jsx(Text, { fontSize: "$3", color: "$soft", text: "center", children: children }) }));
111
+ }
112
+ function Act({ icon: Icon, label, onPress, busy, }) {
113
+ return (_jsx(Box, { render: "button", onClick: onPress, "aria-label": label, px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Icon, { size: 13, "aria-hidden": true }), _jsx(Text, { fontSize: "$1", color: "$soft", children: busy ? '…' : label })] }) }));
114
+ }
115
+ function Row({ box, onResume, onStop, onEnd, busy, }) {
116
+ const rate = HOURLY[(box.class ?? 'exec')] ?? HOURLY.exec;
117
+ return (_jsxs(YStack, { gap: "$2", p: "$3", borderWidth: 1, borderColor: "$borderColor", rounded: "$3", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsxs(Text, { fontSize: "$3", color: "$ink", flex: 1, numberOfLines: 1, children: [box.class ?? 'exec', box.project ? ` · ${box.project}` : ''] }), _jsxs(Text, { fontSize: "$1", color: "$soft", children: [usd(rate), "/hr list"] })] }), _jsxs(XStack, { gap: "$2", items: "baseline", flexWrap: "wrap", children: [_jsxs(Text, { fontSize: "$1", color: "$soft", children: [box.status ?? 'unknown', box.runtime ? ` · ${box.runtime}` : ''] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: remaining(box.expiresAt) })] }), box.status === 'error' && box.error ? (_jsx(Text, { fontSize: "$1", color: "$soft", numberOfLines: 2, children: box.error })) : null, _jsxs(XStack, { gap: "$1", children: [_jsx(Act, { icon: Play, label: "Resume", onPress: onResume, busy: busy === 'resume' }), _jsx(Act, { icon: Square, label: "Stop", onPress: onStop, busy: busy === 'stop' }), _jsx(Act, { icon: X, label: "End", onPress: onEnd, busy: busy === 'end' })] })] }));
118
+ }
119
+ export function Boxes() {
120
+ const { client } = useAi();
121
+ // SIGNED IN IS THE CREDENTIAL, NOT THE PROFILE. This gated on `user`, which is
122
+ // the answer to a userinfo REQUEST — and `IamProvider` swallows that request's
123
+ // failure, so a blip or a slow reply left `isAuthenticated` true and `user`
124
+ // null, and this pane told somebody holding a perfectly good token to sign in.
125
+ // The token is what the API accepts; the profile is decoration on top of it.
126
+ const { isAuthenticated, isLoading } = useIam();
127
+ const [boxes, setBoxes] = useState(null);
128
+ const [busy, setBusy] = useState({});
129
+ const [failed, setFailed] = useState(null);
130
+ const ready = Boolean(client && isAuthenticated);
131
+ const reload = useCallback(() => {
132
+ if (!ready || !client)
133
+ return;
134
+ client.sandboxes
135
+ .list()
136
+ .then(setBoxes)
137
+ .catch((e) => setFailed(e.message));
138
+ }, [client, ready]);
139
+ useEffect(reload, [reload]);
140
+ const act = useCallback(async (id, what) => {
141
+ if (!client)
142
+ return;
143
+ setBusy((b) => ({ ...b, [id]: what }));
144
+ try {
145
+ if (what === 'resume')
146
+ await client.sandboxes.lease({ id });
147
+ if (what === 'stop')
148
+ await client.sandboxes.stop({ id });
149
+ if (what === 'end')
150
+ await client.sandboxes.end({ id });
151
+ reload();
152
+ }
153
+ catch (e) {
154
+ setFailed(e.message);
155
+ }
156
+ finally {
157
+ setBusy((b) => {
158
+ const { [id]: _gone, ...rest } = b;
159
+ return rest;
160
+ });
161
+ }
162
+ }, [client, reload]);
163
+ const signedIn = isAuthenticated || hasSession();
164
+ if (isLoading)
165
+ return _jsx(Empty, { children: "\u00A0" });
166
+ if (!signedIn)
167
+ return _jsx(Empty, { children: "Sign in to see the compute your org is holding." });
168
+ if (failed)
169
+ return _jsx(Empty, { children: say(failed, 'this list') });
170
+ if (!boxes)
171
+ return _jsx(Empty, { children: "Reading your leases." });
172
+ return (_jsxs(YStack, { flex: 1, minH: 0, children: [_jsx(ChannelHeader, { name: "Sandboxes", children: _jsxs(Text, { fontSize: "$2", color: "$soft", children: ["from ", usd(HOURLY.exec), "/hr"] }) }), _jsx(Start, { onDone: reload }), boxes.length === 0 ? (_jsx(Empty, { children: "Nothing is running. A sandbox is leased when an agent needs somewhere to work, and billed for the hours it is held." })) : (_jsx(YStack, { flex: 1, minH: 0, overflow: "scroll", p: "$4", gap: "$3", children: boxes.map((box) => (_jsx(Row, { box: box, busy: busy[box.id], onResume: () => void act(box.id, 'resume'), onStop: () => void act(box.id, 'stop'), onEnd: () => void act(box.id, 'end') }, box.id))) }))] }));
173
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ export interface CalendarEvent {
3
+ id: string;
4
+ title: string;
5
+ type: 'meeting' | 'bot_automation' | 'agent_query';
6
+ date: string;
7
+ time: string;
8
+ duration: string;
9
+ botName?: string;
10
+ meetUrl?: string;
11
+ description?: string;
12
+ cronPattern?: string;
13
+ status: 'scheduled' | 'running' | 'completed';
14
+ }
15
+ export declare function CalScheduler(): React.JSX.Element;
@@ -0,0 +1,196 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect } from 'react';
4
+ import { Calendar as CalendarIcon, Plus, Bot, Video, ExternalLink, Zap, Trash2, X, } from 'lucide-react';
5
+ import { Text, View, XStack, YStack } from '@hanzo/gui';
6
+ import { Button } from '@hanzo/ui';
7
+ const DEFAULT_EVENTS = [
8
+ {
9
+ id: 'evt-1',
10
+ title: 'Daily Morning AI Intelligence Digest',
11
+ type: 'bot_automation',
12
+ date: '2026-09-04',
13
+ time: '09:00',
14
+ duration: '15m',
15
+ botName: 'Chief',
16
+ cronPattern: '0 9 * * 1-5',
17
+ description: 'Scrape breaking frontier AI papers, benchmark shifts, and repo PRs into team Slack.',
18
+ status: 'scheduled',
19
+ },
20
+ {
21
+ id: 'evt-2',
22
+ title: 'Platform Architecture & Inference Sync',
23
+ type: 'meeting',
24
+ date: '2026-09-04',
25
+ time: '18:00',
26
+ duration: '45m',
27
+ meetUrl: 'https://meet.hanzo.ai/platform-architecture',
28
+ botName: 'Hanzo Coder (Bot Co-pilot)',
29
+ description: 'Sync on port 9005 drive server performance and ingress routing.',
30
+ status: 'scheduled',
31
+ },
32
+ {
33
+ id: 'evt-3',
34
+ title: 'Autonomous Outbound Prospecting Run',
35
+ type: 'bot_automation',
36
+ date: '2026-09-04',
37
+ time: '11:00',
38
+ duration: '60m',
39
+ botName: 'Sales Outbound',
40
+ cronPattern: '0 11 * * 1-5',
41
+ description: 'Extract 50 lookalike enterprise leads and prepare personalized drafts.',
42
+ status: 'scheduled',
43
+ },
44
+ {
45
+ id: 'evt-4',
46
+ title: 'Hourly GitHub PR & Typecheck Review',
47
+ type: 'agent_query',
48
+ date: '2026-09-04',
49
+ time: '14:00',
50
+ duration: '20m',
51
+ botName: 'Hanzo Coder',
52
+ cronPattern: '0 * * * *',
53
+ description: 'Verify type safety and lint across all open PRs in hanzoai organization.',
54
+ status: 'scheduled',
55
+ },
56
+ {
57
+ id: 'evt-5',
58
+ title: 'Research Review',
59
+ type: 'meeting',
60
+ date: '2026-09-04',
61
+ time: '20:00',
62
+ duration: '60m',
63
+ meetUrl: 'https://meet.hanzo.ai/research-review',
64
+ botName: 'Hanzo Researcher',
65
+ description: 'Review datasets and proofs for the week’s research runs.',
66
+ status: 'scheduled',
67
+ },
68
+ ];
69
+ /** The public booking pages, one card each on the Booker tab. */
70
+ const BOOKINGS = [
71
+ {
72
+ title: 'Quick Sync',
73
+ duration: '15m',
74
+ slug: '15min',
75
+ description: 'Fast 15-minute alignment or triage call on Hanzo Meet.',
76
+ },
77
+ {
78
+ title: 'Technical Deep Dive',
79
+ duration: '30m',
80
+ slug: '30min',
81
+ description: 'In-depth architectural review, bug diagnosis, or sprint planning.',
82
+ },
83
+ {
84
+ title: 'AI Pair Programming',
85
+ duration: '45m',
86
+ slug: 'pair-ai',
87
+ description: 'Interactive coding session with Hanzo Coder assisting in the terminal.',
88
+ },
89
+ {
90
+ title: 'Executive & Strategy Sync',
91
+ duration: '60m',
92
+ slug: '60min',
93
+ description: 'Quarterly roadmap sync, partnerships, and leadership decision forum.',
94
+ },
95
+ ];
96
+ /** design's smallest rung (`--text-floor`, 10px), below gui's `$1`; gui's font size takes a rung or a number. */
97
+ const FLOOR = 10;
98
+ const MODAL = 'var(--z-modal)';
99
+ /** One field of the dialog: the box every input, select and textarea in it draws. */
100
+ const FIELD = {
101
+ width: '100%',
102
+ py: 8,
103
+ bg: '$background',
104
+ borderWidth: 1,
105
+ borderColor: 'var(--border-control)',
106
+ rounded: 6,
107
+ color: '$ink',
108
+ fontSize: '$2',
109
+ };
110
+ /** A dialog label. */
111
+ const LABEL = { render: 'label', fontSize: '$1', color: '$soft', mb: 4, display: 'block' };
112
+ export function CalScheduler() {
113
+ const [events, setEvents] = useState(() => {
114
+ if (typeof window !== 'undefined') {
115
+ try {
116
+ const stored = localStorage.getItem('hanzo_calendar_events_v1');
117
+ if (stored)
118
+ return JSON.parse(stored);
119
+ }
120
+ catch { }
121
+ }
122
+ return DEFAULT_EVENTS;
123
+ });
124
+ const [activeTab, setActiveTab] = useState('scheduler');
125
+ const [filterType, setFilterType] = useState('all');
126
+ const [showModal, setShowModal] = useState(false);
127
+ const [newEvent, setNewEvent] = useState({
128
+ title: '',
129
+ type: 'bot_automation',
130
+ date: '2026-09-04',
131
+ time: '10:00',
132
+ duration: '30m',
133
+ botName: 'Chief',
134
+ description: '',
135
+ });
136
+ useEffect(() => {
137
+ try {
138
+ localStorage.setItem('hanzo_calendar_events_v1', JSON.stringify(events));
139
+ }
140
+ catch { }
141
+ }, [events]);
142
+ const filteredEvents = events.filter((e) => {
143
+ if (filterType === 'all')
144
+ return true;
145
+ return e.type === filterType;
146
+ });
147
+ const handleCreateEvent = (e) => {
148
+ e.preventDefault();
149
+ if (!newEvent.title.trim())
150
+ return;
151
+ const created = {
152
+ id: `evt-${Date.now()}`,
153
+ title: newEvent.title.trim(),
154
+ type: newEvent.type,
155
+ date: newEvent.date,
156
+ time: newEvent.time,
157
+ duration: newEvent.duration,
158
+ botName: newEvent.botName,
159
+ description: newEvent.description,
160
+ meetUrl: newEvent.type === 'meeting'
161
+ ? `https://meet.hanzo.ai/${newEvent.title.toLowerCase().replace(/[^a-z0-9]/g, '-')}`
162
+ : undefined,
163
+ status: 'scheduled',
164
+ };
165
+ setEvents((prev) => [created, ...prev]);
166
+ setShowModal(false);
167
+ setNewEvent({
168
+ title: '',
169
+ type: 'bot_automation',
170
+ date: '2026-09-04',
171
+ time: '10:00',
172
+ duration: '30m',
173
+ botName: 'Chief',
174
+ description: '',
175
+ });
176
+ };
177
+ const handleDeleteEvent = (id) => {
178
+ setEvents((prev) => prev.filter((e) => e.id !== id));
179
+ };
180
+ return (_jsxs(YStack, { width: "100%", height: "100%", bg: "$background", overflowX: "auto", overflowY: "auto", children: [_jsxs(XStack, { minH: 56, px: 24, py: 8, gap: 10, flexWrap: "wrap", bg: "$panel", borderBottomWidth: 1, borderBottomColor: "$borderColor", items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: 12, shrink: 1, minW: "auto", children: [_jsx(XStack, { width: 32, height: 32, rounded: 8, bg: "$raised", items: "center", justify: "center", shrink: 1, minW: "auto", children: _jsx(CalendarIcon, { size: 18, color: "var(--foreground)" }) }), _jsxs(YStack, { shrink: 1, minW: "auto", children: [_jsxs(XStack, { items: "center", gap: 8, children: [_jsx(Text, { fontSize: "$4", fontWeight: "700", color: "$ink", children: "Hanzo Calendar & Scheduler" }), _jsx(Text, { fontSize: FLOOR, fontWeight: "700", bg: "$hover", color: "$soft", borderWidth: 1, borderColor: "$borderColor", px: 6, py: 1, rounded: 4, children: "cal.hanzo.ai" })] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: "Full unified scheduler across meetings, bot routines, automated crons, and query triggers" })] })] }), _jsxs(XStack, { items: "center", gap: 10, shrink: 1, minW: "auto", flexWrap: "wrap", children: [_jsx(XStack, { items: "center", gap: 2, bg: "$hover", rounded: 8, p: 2, borderWidth: 1, borderColor: "$borderColor", shrink: 1, minW: "auto", children: [
181
+ { id: 'scheduler', label: 'Master Schedule' },
182
+ { id: 'automations', label: 'Bot Crons' },
183
+ { id: 'booker', label: 'Public Booker' },
184
+ ].map((tab) => (_jsx(Button, { size: "sm", variant: activeTab === tab.id ? 'primary' : 'ghost', ...(activeTab === tab.id ? null : { color: '$soft' }), "aria-pressed": activeTab === tab.id, onClick: () => setActiveTab(tab.id), children: tab.label }, tab.id))) }), _jsxs(Button, { variant: "primary", size: "sm", onClick: () => setShowModal(true), children: [_jsx(Plus, { size: 14 }), "Schedule Event / Routine"] })] })] }), activeTab === 'booker' ? (_jsxs(YStack, { flex: 1, p: 28, overflowY: "auto", gap: 20, children: [_jsxs(YStack, { gap: 4, children: [_jsx(Text, { fontSize: "$7", fontWeight: "800", color: "$ink", children: "Public Booking Pages" }), _jsxs(Text, { fontSize: "$2", color: "$soft", children: ["Shareable links powered by the Cal.com fork at ", _jsx(Text, { render: _jsx("a", { href: "https://cal.hanzo.ai", target: "_blank", rel: "noopener noreferrer" }), color: "$ink", textDecorationLine: "underline", children: "cal.hanzo.ai" }), ". Allows teammates, clients, and automated agents to book dedicated time slots."] })] }), _jsx(View, { display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))", gap: 16, children: BOOKINGS.map((card) => (_jsxs(YStack, { bg: "$panel", borderWidth: 1, borderColor: "$borderColor", rounded: 10, p: 18, gap: 12, children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsx(Text, { fontSize: "$4", fontWeight: "700", color: "$ink", children: card.title }), _jsx(Text, { fontSize: "$1", fontWeight: "700", bg: "$hover", px: 8, py: 2, rounded: 4, color: "$soft", children: card.duration })] }), _jsx(Text, { fontSize: "$2", color: "$soft", lineHeight: "$2", children: card.description }), _jsxs(Text, { px: 10, py: 8, bg: "$sunken", rounded: 6, fontSize: "$1", fontFamily: "$mono", color: "$soft", children: ["https://cal.hanzo.ai/alex/", card.slug] }), _jsxs(XStack, { gap: 8, mt: 4, children: [_jsx(Button, { size: "sm", flex: 1, onClick: () => {
185
+ navigator.clipboard.writeText(`https://cal.hanzo.ai/alex/${card.slug}`);
186
+ }, children: "Copy Link" }), _jsx(Button, { asChild: true, variant: "primary", size: "sm", children: _jsxs("a", { href: `https://cal.hanzo.ai/alex/${card.slug}`, target: "_blank", rel: "noopener noreferrer", children: ["Open", _jsx(ExternalLink, { size: 12 })] }) })] })] }, card.slug))) })] })) : (_jsxs(YStack, { flex: 1, overflow: "hidden", children: [_jsxs(XStack, { px: 24, py: 12, bg: "$panel", borderBottomWidth: 1, borderBottomColor: "$borderColor", items: "center", justify: "space-between", flexWrap: "wrap", gap: 8, children: [_jsxs(XStack, { items: "center", gap: 8, shrink: 1, minW: "auto", flexWrap: "wrap", children: [_jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$soft", children: "Filter:" }), [
187
+ { id: 'all', label: 'All Items' },
188
+ { id: 'meeting', label: 'Human Meetings', icon: Video },
189
+ { id: 'bot_automation', label: 'Bot Routines', icon: Bot },
190
+ { id: 'agent_query', label: 'Agent Queries', icon: Zap },
191
+ ].map((f) => (_jsxs(Button, { size: "sm", variant: filterType === f.id ? 'secondary' : 'ghost', color: filterType === f.id ? '$ink' : '$soft', "aria-pressed": filterType === f.id, onClick: () => setFilterType(f.id), children: ['icon' in f ? _jsx(f.icon, { size: 13, "aria-hidden": true }) : null, f.label] }, f.id)))] }), _jsxs(Text, { fontSize: "$1", color: "$faint", children: ["Showing ", filteredEvents.length, " scheduled events & routines"] })] }), _jsx(YStack, { flex: 1, overflowY: "auto", px: 24, py: 20, gap: 10, children: filteredEvents.map((evt) => {
192
+ const isMeeting = evt.type === 'meeting';
193
+ const isAutomation = evt.type === 'bot_automation';
194
+ return (_jsxs(XStack, { bg: "$panel", borderWidth: 1, borderColor: "$borderColor", rounded: 10, px: 20, py: 16, items: "center", justify: "space-between", flexWrap: "wrap", gap: 16, children: [_jsxs(XStack, { items: "center", gap: 16, minW: 180, shrink: 1, children: [_jsx(XStack, { width: 40, height: 40, rounded: 8, bg: "$raised", items: "center", justify: "center", children: isMeeting ? _jsx(Video, { size: 20, color: "var(--foreground)" }) : isAutomation ? _jsx(Bot, { size: 20, color: "var(--foreground)" }) : _jsx(Zap, { size: 20, color: "var(--foreground)" }) }), _jsxs(YStack, { shrink: 1, minW: "auto", children: [_jsx(Text, { fontSize: "$3", fontWeight: "700", color: "$ink", children: evt.time }), _jsxs(Text, { fontSize: "$1", color: "$faint", children: [evt.date, " \u00B7 ", evt.duration] })] })] }), _jsxs(View, { flex: 1, minW: 240, display: "block", children: [_jsxs(XStack, { items: "center", gap: 8, mb: 2, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: evt.title }), _jsx(Text, { fontSize: FLOOR, fontWeight: "700", bg: "$hover", color: "$soft", px: 6, py: 1, rounded: 4, textTransform: "uppercase", children: evt.type.replace('_', ' ') })] }), _jsx(Text, { display: "block", fontSize: "$2", color: "$soft", mb: 4, children: evt.description }), _jsxs(XStack, { items: "center", gap: 12, children: [evt.botName ? _jsxs(Text, { fontSize: "$1", color: "$faint", children: ["Bot: ", _jsx(Text, { render: "strong", fontSize: "$1", color: "$quiet", children: evt.botName })] }) : null, evt.cronPattern ? _jsxs(Text, { fontSize: "$1", color: "$faint", children: ["Cron: ", _jsx(Text, { render: "code", color: "$ink", children: evt.cronPattern })] }) : null, evt.meetUrl ? _jsxs(Text, { fontSize: "$1", color: "$faint", children: ["Link: ", _jsx(Text, { render: "code", color: "$ink", children: evt.meetUrl })] }) : null] })] }), _jsxs(XStack, { items: "center", gap: 8, shrink: 1, minW: "auto", children: [isMeeting ? (_jsx(Button, { asChild: true, variant: "primary", size: "sm", children: _jsxs("a", { href: "/meet", children: [_jsx(Video, { size: 13 }), "Join /meet"] }) })) : (_jsx(Button, { asChild: true, variant: "primary", size: "sm", children: _jsxs("a", { href: "/bots", children: [_jsx(Bot, { size: 13 }), "View /bots"] }) })), _jsx(Button, { variant: "ghost", size: "icon-sm", color: "$soft", onClick: () => handleDeleteEvent(evt.id), "aria-label": "Remove schedule", title: "Remove schedule", children: _jsx(Trash2, { size: 14 }) })] })] }, evt.id));
195
+ }) })] })), showModal ? (_jsx(XStack, { position: "fixed", inset: 0, bg: "var(--surface-scrim)", backdropFilter: "blur(8px)", items: "center", justify: "center", z: MODAL, children: _jsxs(YStack, { width: "100%", maxW: 460, bg: "var(--popover)", borderWidth: 1, borderColor: "var(--border-control)", rounded: 12, p: 24, gap: 16, boxShadow: "var(--shadow-floating)", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsx(Text, { fontSize: "$4", fontWeight: "700", color: "$ink", children: "Schedule Event or Bot Routine" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", mr: -8, onClick: () => setShowModal(false), "aria-label": "Close", children: _jsx(X, { size: 16, "aria-hidden": true }) })] }), _jsxs(YStack, { render: "form", onSubmit: handleCreateEvent, gap: 12, children: [_jsxs(View, { display: "block", children: [_jsx(Text, { ...LABEL, children: "Title" }), _jsx(Text, { render: _jsx("input", { type: "text", value: newEvent.title, onChange: (e) => setNewEvent({ ...newEvent, title: e.target.value }), placeholder: "e.g. Daily Security Audit / Team Sync", required: true }), ...FIELD, px: 12 })] }), _jsxs(View, { display: "block", children: [_jsx(Text, { ...LABEL, children: "Schedule Type" }), _jsxs(Text, { render: _jsx("select", { value: newEvent.type, onChange: (e) => setNewEvent({ ...newEvent, type: e.target.value }) }), ...FIELD, px: 12, children: [_jsx("option", { value: "bot_automation", children: "Automated Bot Cron Routine" }), _jsx("option", { value: "meeting", children: "Human Video Meeting (/meet)" }), _jsx("option", { value: "agent_query", children: "Recurring Agent Query" })] })] }), _jsxs(View, { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10, children: [_jsxs(View, { display: "block", children: [_jsx(Text, { ...LABEL, children: "Date" }), _jsx(Text, { render: _jsx("input", { type: "date", value: newEvent.date, onChange: (e) => setNewEvent({ ...newEvent, date: e.target.value }) }), ...FIELD, px: 8 })] }), _jsxs(View, { display: "block", children: [_jsx(Text, { ...LABEL, children: "Time" }), _jsx(Text, { render: _jsx("input", { type: "time", value: newEvent.time, onChange: (e) => setNewEvent({ ...newEvent, time: e.target.value }) }), ...FIELD, px: 8 })] })] }), _jsxs(View, { display: "block", children: [_jsx(Text, { ...LABEL, children: "Assigned Bot" }), _jsxs(Text, { render: _jsx("select", { value: newEvent.botName, onChange: (e) => setNewEvent({ ...newEvent, botName: e.target.value }) }), ...FIELD, px: 12, children: [_jsx("option", { value: "Chief", children: "Chief" }), _jsx("option", { value: "Sales Outbound", children: "Sales Outbound" }), _jsx("option", { value: "Inbox Manager", children: "Inbox Manager" }), _jsx("option", { value: "Account Manager", children: "Account Manager" }), _jsx("option", { value: "Talent Scout", children: "Talent Scout" }), _jsx("option", { value: "Expense Manager", children: "Expense Manager" }), _jsx("option", { value: "Offsite crew", children: "Offsite crew" })] })] }), _jsxs(View, { display: "block", children: [_jsx(Text, { ...LABEL, children: "Description / Instruction" }), _jsx(Text, { render: _jsx("textarea", { value: newEvent.description, onChange: (e) => setNewEvent({ ...newEvent, description: e.target.value }), placeholder: "Details or cron automation prompt...", rows: 3 }), ...FIELD, px: 12, "$platform-web": { resize: 'none' } })] }), _jsxs(XStack, { justify: "flex-end", gap: 8, mt: 8, children: [_jsx(Button, { type: "button", onClick: () => setShowModal(false), children: "Cancel" }), _jsx(Button, { type: "submit", variant: "primary", children: "Save Schedule" })] })] })] }) })) : null] }));
196
+ }
package/dist/Chat.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ export declare function Chat(): import("react").JSX.Element;
2
+ /**
3
+ * The same conversation, embedded in a page that is not /chat.
4
+ *
5
+ * R8: there were TWO live chat surfaces with different consent semantics — this
6
+ * one asks at SEND and fails closed, the other gated on ARRIVAL through a
7
+ * separate copy of the consent code. Two answers to one question is how they
8
+ * drift, so /models mounts this and the second surface is gone. It carries no
9
+ * heading of its own: the host page already has one, and a second `<h1>` is the
10
+ * defect that took the landings off /chat and /dev.
11
+ *
12
+ * The host supplies the `AiProvider`, because the host is what knows whether it
13
+ * already has one.
14
+ */
15
+ export declare function Ask({ placeholder, idle, }: {
16
+ placeholder?: string;
17
+ idle?: React.ReactNode;
18
+ }): import("react").JSX.Element;
19
+ /**
20
+ * The agents a question speaks to: those it names, or the whole room when it
21
+ * names nobody — the rule the room's brief gives the model (cast.tsx `brief`).
22
+ */
23
+ export declare function addressed(question: string, room: string[]): string[];
24
+ /**
25
+ * Which kind of refusal a failed turn met, read off the platform's own codes:
26
+ * the caller's wallet (`insufficient_balance`, the ai service's spend check), a visitor's
27
+ * spent free messages (`public_allowance_spent`), or a model provider that did
28
+ * not answer (`providers_exhausted`, or any 5xx that is not the balance lookup).
29
+ */
30
+ export declare function refusalOf(error: Error): {
31
+ wallet: boolean;
32
+ visitor: boolean;
33
+ provider: boolean;
34
+ };
35
+ /** The agreement free asks for, in @hanzo/ai's words so every surface says one thing. */