@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
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Sharing a conversation by link.
3
+ *
4
+ * The platform makes a read-only link to one conversation and answers its token
5
+ * once (github.com/hanzoai/agent share.go, under /v1/agent/chat). It keeps only
6
+ * the token's hash, so the token is held here, in this person's browser, for
7
+ * the conversation it opens — `own()` in lib/auth/session.ts removes it with
8
+ * every other `hanzo*` key when a different person signs in.
9
+ *
10
+ * A shared chat opens for its owner and for the people who open the link signed
11
+ * in, and for nobody else. A reader who is not signed in is told the title and
12
+ * asked to sign in; a signed-in reader is recorded as a viewer the owner can
13
+ * see, reads the chat, and finds it afterwards under Shared with you, read by
14
+ * its share id (`/chat/shared?s=<id>`) with no token at all. The token names a
15
+ * chat and authenticates nobody: every read carries the reader's own bearer.
16
+ *
17
+ * The token rides the address as the FRAGMENT (`/chat/shared#<token>`), which a
18
+ * browser never sends to any server. It leaves the address before anything on
19
+ * the page can read it: `SEAL`, the first script in the document head, moves it
20
+ * into this tab's sessionStorage and replaces the address with one that has no
21
+ * fragment, before the analytics and ad tags configure themselves. It reaches
22
+ * the platform only in a request body, and survives the sign-in round trip in
23
+ * sessionStorage — never back in an address.
24
+ */
25
+ import { api } from './api.js';
26
+ import { live, scope } from './session.js';
27
+ /** The page a shared conversation is read on. */
28
+ export const SHARED = '/chat/shared';
29
+ /** Where this tab holds a link's token between opening it and reading it. */
30
+ export const OPENING = 'hanzo:share:open';
31
+ /**
32
+ * The document-head script that takes a share token out of the address. On any
33
+ * spelling of /chat/shared with a fragment — and on any page whose fragment has
34
+ * a share token's shape, the not-found page included — the fragment goes to
35
+ * sessionStorage (and to `window.hzShare` for a browser that refuses storage)
36
+ * and the address is replaced by the same path and query with no fragment. It
37
+ * runs as the document is parsed and again on every `hashchange` and
38
+ * `popstate`, with its listeners registered before any tag's, so a link pasted
39
+ * into an open tab is taken out before a tag's own history listener reads the
40
+ * address. First in the head, so no tag and no module ever sees an address
41
+ * carrying a token.
42
+ */
43
+ export const SEAL = `(function(){function s(){try{var l=location,h=l.hash;if(h.length<2)return;var t=h.slice(1);try{t=decodeURIComponent(t)}catch(_){}var p=l.pathname.toLowerCase().replace(/\\/+/g,'/').replace(/\\/index\\.html$/,'').replace(/\\.html$/,'').replace(/\\/$/,'');if(p!=='${SHARED}'&&!/^[A-Za-z0-9_-]{43}$/.test(t))return;window.hzShare=t;try{sessionStorage.setItem('${OPENING}',t)}catch(_){}history.replaceState(null,'',l.pathname+l.search)}catch(_){}}s();addEventListener('hashchange',s);addEventListener('popstate',s)})()`;
44
+ /** `SEAL` for a fragment the head's listeners did not take: the token goes to
45
+ * this tab and the address loses the fragment. */
46
+ export function take() {
47
+ try {
48
+ const hash = window.location.hash;
49
+ if (hash.length < 2)
50
+ return;
51
+ let token = hash.slice(1);
52
+ try {
53
+ token = decodeURIComponent(token);
54
+ }
55
+ catch {
56
+ /* a malformed escape: the fragment as written */
57
+ }
58
+ ;
59
+ window.hzShare = token;
60
+ try {
61
+ window.sessionStorage.setItem(OPENING, token);
62
+ }
63
+ catch {
64
+ /* held on the window for this page */
65
+ }
66
+ history.replaceState(null, '', window.location.pathname + window.location.search);
67
+ }
68
+ catch {
69
+ /* a browser that refuses history: the head script already tried */
70
+ }
71
+ }
72
+ /** The address that opens a share, on this origin unless told otherwise. */
73
+ export function link(token, origin = window.location.origin) {
74
+ return `${origin}${SHARED}#${token}`;
75
+ }
76
+ /** The address a viewer reads a chat shared with them at, without the token. */
77
+ export function address(share) {
78
+ return `${SHARED}?s=${encodeURIComponent(share)}`;
79
+ }
80
+ /** A token this tab is holding for a link it opened and has not read yet, or ''. */
81
+ export function opening() {
82
+ if (typeof window === 'undefined')
83
+ return '';
84
+ try {
85
+ const held = window.sessionStorage.getItem(OPENING);
86
+ if (held)
87
+ return held;
88
+ }
89
+ catch {
90
+ /* a browser that refuses storage: the head left it on the window */
91
+ }
92
+ return window.hzShare ?? '';
93
+ }
94
+ /** Forget the token this tab was holding, once the link has been read. */
95
+ export function opened() {
96
+ try {
97
+ window.sessionStorage.removeItem(OPENING);
98
+ }
99
+ catch {
100
+ /* nothing held */
101
+ }
102
+ delete window.hzShare;
103
+ }
104
+ /** A refusal, carrying the platform's own sentence and status. */
105
+ export class Refused extends Error {
106
+ status;
107
+ constructor(message, status) {
108
+ super(message);
109
+ this.status = status;
110
+ }
111
+ }
112
+ /** The sentence a refusal carries, or one in plain words when it carries none. */
113
+ function sentence(body, status) {
114
+ const b = (body ?? {});
115
+ const said = [b.detail, typeof b.error === 'object' ? b.error?.message : b.error, b.message].find((v) => typeof v === 'string' && v.trim() !== '');
116
+ if (said)
117
+ return said;
118
+ if (status === 401 || status === 403)
119
+ return 'Sign in again to manage sharing.';
120
+ if (status >= 500)
121
+ return 'Sharing is not answering right now. Try again shortly.';
122
+ return 'That did not work. Try again.';
123
+ }
124
+ async function call(path, init) {
125
+ const r = await fetch(`${api()}${path}`, init);
126
+ const text = await r.text();
127
+ let body = null;
128
+ try {
129
+ body = text ? JSON.parse(text) : null;
130
+ }
131
+ catch {
132
+ body = null;
133
+ }
134
+ if (!r.ok)
135
+ throw new Refused(sentence(body, r.status), r.status);
136
+ return body;
137
+ }
138
+ const base = (thread) => `/v1/agent/chat/conversations/${encodeURIComponent(thread)}/shares`;
139
+ /** The owner's side: make, list and revoke the links to one conversation, and
140
+ * remove one viewer from a link. */
141
+ export const shares = {
142
+ list: async (thread) => (await call(base(thread), { headers: scope() })).shares ?? [],
143
+ make: (thread) => call(base(thread), { method: 'POST', headers: { 'Content-Type': 'application/json', ...scope() }, body: '{}' }),
144
+ revoke: (thread, id) => call(`${base(thread)}/${encodeURIComponent(id)}`, { method: 'DELETE', headers: scope() }),
145
+ unview: (thread, id, viewer) => call(`${base(thread)}/${encodeURIComponent(id)}/viewers/${encodeURIComponent(viewer)}`, { method: 'DELETE', headers: scope() }),
146
+ };
147
+ /** The reader's bearer and the organization they are working in: an owner who
148
+ * opens their own link from the organization they shared it in is its owner
149
+ * there, and not a viewer of it. Nothing about the reader's organization is
150
+ * read or changed. */
151
+ function reader() {
152
+ if (!live())
153
+ return {};
154
+ const h = scope();
155
+ return h.Authorization ? h : {};
156
+ }
157
+ /** Open a link, or null when it opens nothing (never made, revoked, or closed
158
+ * to this reader). `open` is the reader's go-ahead to be recorded as a viewer,
159
+ * given after they were told who shared it and that their name is shown. */
160
+ export async function read(token, open = false) {
161
+ try {
162
+ return await call('/v1/agent/chat/shares/read', {
163
+ method: 'POST',
164
+ headers: { 'Content-Type': 'application/json', ...reader() },
165
+ body: JSON.stringify({ token, open }),
166
+ });
167
+ }
168
+ catch (e) {
169
+ if (e instanceof Refused && e.status === 404)
170
+ return null;
171
+ throw e;
172
+ }
173
+ }
174
+ /** Read a chat shared with the reader by its share id, or null when it is no
175
+ * longer open to them. */
176
+ export async function readShared(share) {
177
+ try {
178
+ return await call(`/v1/agent/chat/shared/${encodeURIComponent(share)}`, { headers: reader() });
179
+ }
180
+ catch (e) {
181
+ if (e instanceof Refused && e.status === 404)
182
+ return null;
183
+ throw e;
184
+ }
185
+ }
186
+ /** The chats shared with the reader, most recently opened first. */
187
+ export async function sharedWithMe() {
188
+ return (await call('/v1/agent/chat/shared', { headers: reader() })).shared ?? [];
189
+ }
190
+ /** Where this browser keeps the token of the link it last made for a conversation. */
191
+ const held = (thread) => `hanzo:share:${thread}`;
192
+ /** The link this browser holds for a conversation, if its share is still live. */
193
+ export function heldToken(thread, live) {
194
+ try {
195
+ const raw = window.localStorage.getItem(held(thread));
196
+ const got = raw ? JSON.parse(raw) : null;
197
+ if (got?.id && got.token && live.some((s) => s.id === got.id))
198
+ return { id: got.id, token: got.token };
199
+ }
200
+ catch {
201
+ /* nothing held, or a store that refuses */
202
+ }
203
+ return null;
204
+ }
205
+ /** Hold a new link's token for its conversation; null forgets it. */
206
+ export function hold(thread, value) {
207
+ try {
208
+ if (value)
209
+ window.localStorage.setItem(held(thread), JSON.stringify(value));
210
+ else
211
+ window.localStorage.removeItem(held(thread));
212
+ }
213
+ catch {
214
+ /* the link still works; this browser just cannot copy it again */
215
+ }
216
+ }
@@ -0,0 +1,14 @@
1
+ import { IAM } from '@hanzo/iam';
2
+ /** Whether the page in front of us is on such an origin. */
3
+ export declare const here: () => boolean;
4
+ /** The IAM client for this page; the token exchange at the callback shares its PKCE store. */
5
+ export declare function useClient(): IAM;
6
+ /** Where a sign-out lands: this site's sign-in, which does not ask IAM for the session just ended. */
7
+ export declare const OUT = "/login?from=logout";
8
+ /**
9
+ * Sign-out on this site: @hanzo/ui/auth's `signOut` — the tokens handed back to
10
+ * IAM's /v1/iam routes, IAM's session here ended, the local session cleared —
11
+ * counted as logout_completed, and then /login?from=logout, where the person can
12
+ * sign straight back in. IAM's own logout page is never visited.
13
+ */
14
+ export declare function useSignOut(): () => Promise<void>;
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+ /**
3
+ * IAM, called from this page. The embedded sign-in, the silent check for a
4
+ * session and sign-out all go through `useClient`, whose credential calls are
5
+ * answered on this origin where it answers them (universe static-sites
6
+ * `iam-on-hanzo-ai`): that is also where IAM keeps its session cookie, so the
7
+ * session a person signs in with here is the one a later visit finds, and the
8
+ * one sign-out ends. Nobody is sent to hanzo.id for any of it.
9
+ */
10
+ import { useMemo } from 'react';
11
+ import { IAM } from '@hanzo/iam';
12
+ import { useIam } from '@hanzo/iam/react';
13
+ import { useSignOut as useUiSignOut } from '@hanzo/ui/auth';
14
+ import { track } from './tags.js';
15
+ import { APEX } from './host.js';
16
+ /**
17
+ * The hosts that answer IAM's sign-in and sign-out calls on their own origin:
18
+ * every host this export is served on and signs in at (universe static-sites
19
+ * `iam-on-hanzo-ai`), and the dev server's /v1 proxy.
20
+ */
21
+ const HOSTS = new Set([...APEX, 'cloud.hanzo.ai', 'hanzo.bot', 'bot.hanzo.ai', 'localhost', '127.0.0.1']);
22
+ /** Whether the page in front of us is on such an origin. */
23
+ export const here = () => typeof window !== 'undefined' && HOSTS.has(window.location.hostname);
24
+ /** The IAM client for this page; the token exchange at the callback shares its PKCE store. */
25
+ export function useClient() {
26
+ const { config } = useIam();
27
+ const local = here();
28
+ return useMemo(() => new IAM(local ? { ...config, proxyBaseUrl: window.location.origin } : config), [config, local]);
29
+ }
30
+ /** Where a sign-out lands: this site's sign-in, which does not ask IAM for the session just ended. */
31
+ export const OUT = '/login?from=logout';
32
+ /**
33
+ * Sign-out on this site: @hanzo/ui/auth's `signOut` — the tokens handed back to
34
+ * IAM's /v1/iam routes, IAM's session here ended, the local session cleared —
35
+ * counted as logout_completed, and then /login?from=logout, where the person can
36
+ * sign straight back in. IAM's own logout page is never visited.
37
+ */
38
+ export function useSignOut() {
39
+ return useUiSignOut({ to: OUT, track });
40
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The funnel's one call. `track` is @hanzo/event's: a moment reaches our stream
3
+ * (POST /v1/event, the one ingest) and every browser tag the visitor's consent
4
+ * and region allow, under one event_id. The tags come from the site's tag
5
+ * config (GET /v1/project/tags, which also says which consent rule binds this
6
+ * visitor); no platform id lives in this repo. Our stream stays the record every
7
+ * Hanzo dashboard reads, and cloud forwards its copy server-side.
8
+ *
9
+ * `startTags` runs once, from app/providers.tsx. `attach` hands `track` the
10
+ * page's one client, from inside its provider.
11
+ */
12
+ import { mirror, startTags, tagsReady, visit, type Analytics } from '@hanzo/event';
13
+ export { mirror, startTags, tagsReady, visit };
14
+ /** The sites one visit can cross; GA4 keeps it one session across them. */
15
+ export declare const DOMAINS: string[];
16
+ /** Hands track() the one @hanzo/event client, from inside its provider. */
17
+ export declare function attach(client: Analytics): void;
18
+ /**
19
+ * `url` with the visitor's identity on it when it leads to another Hanzo host
20
+ * (hanzo.id, hanzo.app), so the journey across the hop is one visitor
21
+ * (@hanzo/event link). Any other address comes back as it was.
22
+ */
23
+ export declare function link(url: string): string;
24
+ /** `link` for the redirect to IAM: the same, and the queue is sent before the page leaves. */
25
+ export declare function authorize(url: string): string;
26
+ /** One funnel moment, to every place it is counted, under one event_id. */
27
+ export declare function track(name: string, params?: Record<string, unknown>): void;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The funnel's one call. `track` is @hanzo/event's: a moment reaches our stream
3
+ * (POST /v1/event, the one ingest) and every browser tag the visitor's consent
4
+ * and region allow, under one event_id. The tags come from the site's tag
5
+ * config (GET /v1/project/tags, which also says which consent rule binds this
6
+ * visitor); no platform id lives in this repo. Our stream stays the record every
7
+ * Hanzo dashboard reads, and cloud forwards its copy server-side.
8
+ *
9
+ * `startTags` runs once, from app/providers.tsx. `attach` hands `track` the
10
+ * page's one client, from inside its provider.
11
+ */
12
+ import { mirror, startTags, tagsReady, track as fire, visit } from '@hanzo/event';
13
+ export { mirror, startTags, tagsReady, visit };
14
+ /** The sites one visit can cross; GA4 keeps it one session across them. */
15
+ export const DOMAINS = [
16
+ 'hanzo.ai',
17
+ 'hanzo.app',
18
+ 'hanzo.chat',
19
+ 'pay.hanzo.ai',
20
+ 'hanzo.id',
21
+ 'hanzo.build',
22
+ 'docs.hanzo.ai',
23
+ 'platform.hanzo.ai',
24
+ 'cal.hanzo.ai',
25
+ ];
26
+ let stream;
27
+ /** Hands track() the one @hanzo/event client, from inside its provider. */
28
+ export function attach(client) {
29
+ stream = client;
30
+ }
31
+ /**
32
+ * `url` with the visitor's identity on it when it leads to another Hanzo host
33
+ * (hanzo.id, hanzo.app), so the journey across the hop is one visitor
34
+ * (@hanzo/event link). Any other address comes back as it was.
35
+ */
36
+ export function link(url) {
37
+ return stream ? stream.link(url) : url;
38
+ }
39
+ /** `link` for the redirect to IAM: the same, and the queue is sent before the page leaves. */
40
+ export function authorize(url) {
41
+ return stream ? stream.authorize(url) : url;
42
+ }
43
+ /** One funnel moment, to every place it is counted, under one event_id. */
44
+ export function track(name, params = {}) {
45
+ fire(stream, name, params);
46
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A standalone tap target, as gui props: a box with its content in a row, and
3
+ * 44px on its smaller side under a coarse pointer (WCAG 2.5.5, a thumb). A
4
+ * mouse keeps the control's own size. A link inside a sentence is not one —
5
+ * stretching it to 44px would tear the line it sits in.
6
+ *
7
+ * Spread it on the gui element that is the control: `<XStack render="a" {...tap}>`.
8
+ */
9
+ export declare const tap: {
10
+ readonly display: 'inline-flex';
11
+ readonly flexDirection: 'row';
12
+ readonly items: 'center';
13
+ readonly $touchable: {
14
+ readonly minH: 44;
15
+ readonly minW: 44;
16
+ };
17
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * A standalone tap target, as gui props: a box with its content in a row, and
3
+ * 44px on its smaller side under a coarse pointer (WCAG 2.5.5, a thumb). A
4
+ * mouse keeps the control's own size. A link inside a sentence is not one —
5
+ * stretching it to 44px would tear the line it sits in.
6
+ *
7
+ * Spread it on the gui element that is the control: `<XStack render="a" {...tap}>`.
8
+ */
9
+ export const tap = { display: 'inline-flex', flexDirection: 'row', items: 'center', $touchable: { minH: 44, minW: 44 } };
@@ -0,0 +1,115 @@
1
+ /** The plan, as billing names it. */
2
+ export interface TierLimits {
3
+ name: string;
4
+ displayName: string;
5
+ dailyCreditsCents: number;
6
+ maxAgents: number;
7
+ /** MaxAgents 0 means "no ceiling" rather than "no agents" — the reading a
8
+ * bare zero cannot carry. The server publishes it because the zero is
9
+ * ambiguous, so nothing here re-derives it. */
10
+ unlimitedAgents: boolean;
11
+ allowedModels?: string[];
12
+ }
13
+ /** Whole USD cents, every field. */
14
+ export interface TierBalance {
15
+ currency: string;
16
+ prepaidAvailable: number;
17
+ creditsRemaining: number;
18
+ dailyRemaining: number;
19
+ /** THE ONE FIGURE TO COMPARE AGAINST ZERO: the ledger's `available`, which
20
+ * `readTier` states from GET /v1/billing/balance. */
21
+ effectiveAvailable: number;
22
+ /** The account that figure is the balance OF, as billing resolved the payer —
23
+ * the org's own pool, or a member's wallet inside it. Never derived here. */
24
+ account?: string;
25
+ }
26
+ /** A rate term — "10 requests per minute, 3 left, resets at …". */
27
+ export interface TierWindow {
28
+ span: string;
29
+ limit: number;
30
+ used: number;
31
+ remaining: number;
32
+ resets: string;
33
+ }
34
+ export interface Tier {
35
+ user: string;
36
+ /** The catalog rung the subject is served as — a plan slug like "max-20x",
37
+ * or "" where the account holds no subscription. `tier` is the coarser
38
+ * class (free/dev/enterprise) that slug is served under; this is the row
39
+ * `lib/plans.ts` names, and the one a menu should print. */
40
+ plan: string;
41
+ tier: TierLimits;
42
+ balance: TierBalance;
43
+ windows?: TierWindow[];
44
+ }
45
+ /** Reads the tier and the balance for whoever holds the bearer. The requests
46
+ * name no subject — the credential is the scope — so there is no id to get wrong. */
47
+ export declare function readTier(signal?: AbortSignal): Promise<Tier>;
48
+ /**
49
+ * Whether a tier answer is a paid plan: billing names a paid plan the account's
50
+ * subscription is on, or serves it a paid tier — `pro`, which a plan recorded
51
+ * outside checkout confers, or `enterprise` (the platform org, a partner org).
52
+ * A trial nobody paid for is `starter` and names no plan.
53
+ */
54
+ export declare function paid(t: {
55
+ plan?: string;
56
+ tier?: {
57
+ name?: string;
58
+ };
59
+ } | null | undefined): boolean;
60
+ /** A plan the org subscribes to, as GET /v1/billing/subscriptions lists it. Money in whole cents. */
61
+ export interface Subscription {
62
+ id: string;
63
+ status: string;
64
+ quantity: number;
65
+ currentPeriodEnd: string;
66
+ cancelAtPeriodEnd: boolean;
67
+ plan: {
68
+ id: string;
69
+ name: string;
70
+ price: number;
71
+ currency: string;
72
+ interval: string;
73
+ };
74
+ }
75
+ /**
76
+ * The org's live subscription, or null where it holds none — being on no plan
77
+ * is an answer, and a refusal is not: that throws, the way the tier read does.
78
+ */
79
+ export declare function readSubscription(signal?: AbortSignal): Promise<Subscription | null>;
80
+ /**
81
+ * The term as a sentence: "$200/month subscription, renews Oct 30, 2026". The
82
+ * price is per seat, so it is the seats times it; a cancelled term ends rather
83
+ * than renews.
84
+ */
85
+ export declare function renewal(s: Subscription): string;
86
+ /** The live subscription, held: null while unknown, refused, or on no plan. */
87
+ export declare function useSubscription(enabled?: boolean, org?: string | null): Subscription | null;
88
+ /**
89
+ * Whether this account pays for a plan where it works: in the organization it
90
+ * acts in, or, before it has picked one, in any organization its token names.
91
+ * A read that fails or takes longer than PATIENCE counts as no plan, so the
92
+ * plans are offered rather than the app withheld. At most ten organizations
93
+ * are asked.
94
+ */
95
+ export declare function paying(signal?: AbortSignal): Promise<boolean>;
96
+ /**
97
+ * The tier, held. `null` while unknown — which is BOTH "still asking" and
98
+ * "asked and was refused", so `wrong` is kept beside it rather than swallowed:
99
+ * a row cannot choose between saying nothing and saying why without it.
100
+ */
101
+ export declare function useTier(enabled?: boolean, org?: string | null): {
102
+ tier: Tier | null;
103
+ wrong: unknown;
104
+ reload: () => void;
105
+ };
106
+ /**
107
+ * Whether an organization is its owner's personal one, off IAM's own row.
108
+ *
109
+ * The token's `orgs` claim names an organization and the reader's role in it
110
+ * and says nothing of its kind; IAM keeps that on the row (`isPersonal`), and
111
+ * `GET /v1/iam/organizations` answers the rows the caller belongs to. `null`
112
+ * until it is answered for this organization. A refusal reads as `false`, so a
113
+ * room that needs a plan goes on asking for one.
114
+ */
115
+ export declare function usePersonal(org: string | null, enabled?: boolean): boolean | null;