@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,61 @@
1
+ /**
2
+ * What the home reads, and the two reads `@hanzo/ai` publishes no hook for.
3
+ *
4
+ * Sessions, agents, threads and people all have hooks — the home uses those and
5
+ * adds nothing. Projects and connected apps do not, so they go through the
6
+ * client's own transport (`client.http`, which the SDK exposes for exactly
7
+ * this) rather than a second fetch stack with a second answer about who the
8
+ * bearer is.
9
+ *
10
+ * EVERY READER ANSWERS `null` FOR UNKNOWN. An empty array is a fact — the org
11
+ * has none — and a failed or unmade request is not that fact. Collapsing the
12
+ * two is how a card comes to say "0 projects" about a question nobody asked.
13
+ */
14
+ import type { AiClient } from '@hanzo/ai';
15
+ /** A site the org has deployed. */
16
+ export interface Project {
17
+ slug: string;
18
+ name?: string;
19
+ /** Where it serves, when it is serving. */
20
+ liveUrl?: string;
21
+ /** Unix seconds. */
22
+ updatedAt?: number;
23
+ }
24
+ /** A service the org can connect, and whether it has. */
25
+ export interface App {
26
+ id: string;
27
+ name: string;
28
+ connected?: boolean;
29
+ }
30
+ /**
31
+ * Where a teammate is invited.
32
+ *
33
+ * IAM owns invitations and `@hanzo/iam` publishes no method that sends one, so
34
+ * the invite LEAVES this surface for hanzo.id's own org members page rather
35
+ * than this site growing an invite backend of its own. `Shell.tsx` states the
36
+ * same address privately; it should read this one.
37
+ */
38
+ /**
39
+ * WHERE A TEAMMATE IS ACTUALLY ADDED.
40
+ *
41
+ * It pointed at the account settings of the identity service, which is a
42
+ * different question with a similar name: that page is where a person edits
43
+ * THEMSELVES, so pressing Invite people took you off this site and showed you
44
+ * your own settings. This page issues the invitation — it mints the single-use
45
+ * code, files it against the org, and hands back the link to send — so the
46
+ * control and its verb are the same thing again.
47
+ *
48
+ * A PATH, not a URL. It is a page of this app, so it is reached the way every
49
+ * other page of this app is, and the three doors that offer it (the empty
50
+ * room's tile, the Create menu, the sidebar's row) all read this one line.
51
+ */
52
+ export declare const invite: () => string;
53
+ /** The org's projects, newest first. */
54
+ export declare function projects(client: AiClient, signal?: AbortSignal): Promise<Project[]>;
55
+ /**
56
+ * Every app the platform can connect, carrying whether this org has.
57
+ *
58
+ * The route answers the whole catalogue — connected and not — so the caller
59
+ * counts rather than being told a number.
60
+ */
61
+ export declare function apps(client: AiClient, signal?: AbortSignal): Promise<App[]>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * What the home reads, and the two reads `@hanzo/ai` publishes no hook for.
3
+ *
4
+ * Sessions, agents, threads and people all have hooks — the home uses those and
5
+ * adds nothing. Projects and connected apps do not, so they go through the
6
+ * client's own transport (`client.http`, which the SDK exposes for exactly
7
+ * this) rather than a second fetch stack with a second answer about who the
8
+ * bearer is.
9
+ *
10
+ * EVERY READER ANSWERS `null` FOR UNKNOWN. An empty array is a fact — the org
11
+ * has none — and a failed or unmade request is not that fact. Collapsing the
12
+ * two is how a card comes to say "0 projects" about a question nobody asked.
13
+ */
14
+ import { site } from '../where.js';
15
+ /**
16
+ * Where a teammate is invited.
17
+ *
18
+ * IAM owns invitations and `@hanzo/iam` publishes no method that sends one, so
19
+ * the invite LEAVES this surface for hanzo.id's own org members page rather
20
+ * than this site growing an invite backend of its own. `Shell.tsx` states the
21
+ * same address privately; it should read this one.
22
+ */
23
+ /**
24
+ * WHERE A TEAMMATE IS ACTUALLY ADDED.
25
+ *
26
+ * It pointed at the account settings of the identity service, which is a
27
+ * different question with a similar name: that page is where a person edits
28
+ * THEMSELVES, so pressing Invite people took you off this site and showed you
29
+ * your own settings. This page issues the invitation — it mints the single-use
30
+ * code, files it against the org, and hands back the link to send — so the
31
+ * control and its verb are the same thing again.
32
+ *
33
+ * A PATH, not a URL. It is a page of this app, so it is reached the way every
34
+ * other page of this app is, and the three doors that offer it (the empty
35
+ * room's tile, the Create menu, the sidebar's row) all read this one line.
36
+ */
37
+ export const invite = () => site('/account/organization');
38
+ /** The org's projects, newest first. */
39
+ export async function projects(client, signal) {
40
+ const rows = await client.http.json({ path: '/v1/projects', signal });
41
+ if (!Array.isArray(rows))
42
+ return [];
43
+ return [...rows].sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
44
+ }
45
+ /**
46
+ * Every app the platform can connect, carrying whether this org has.
47
+ *
48
+ * The route answers the whole catalogue — connected and not — so the caller
49
+ * counts rather than being told a number.
50
+ */
51
+ export async function apps(client, signal) {
52
+ const out = await client.http.json({ path: '/v1/integrations', signal });
53
+ return out?.providers ?? [];
54
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Which host this export is read on, and what that host keeps.
3
+ *
4
+ * hanzo.ai is the site for a stranger and the app for a member: the web app is
5
+ * `/` once signed in (app/page.tsx), beside /chat. hanzo.team is the
6
+ * workspace, its own app (hanzo-apps/team) with Home at its root. This export
7
+ * still carries the rooms, so a room asked for on the apex is sent to the same
8
+ * room on hanzo.team, and `output: 'export'` has no server to do that, so it is
9
+ * decided in the browser off the hostname, here.
10
+ *
11
+ * Two predicates, one per product: `apex()` for the app, `team()` for the
12
+ * workspace. Every host that is neither (localhost, a preview) draws the whole
13
+ * workspace and admits the way the apex does.
14
+ */
15
+ /** The workspace's own address. */
16
+ export declare const WORKSPACE = "https://hanzo.team";
17
+ /**
18
+ * The workspace, signed in. hanzo.team keeps its own session (its own origin's
19
+ * storage), so a reader signed in here arrives there signed out and is shown the
20
+ * landing. `?signin` asks it to start the sign-in on arrival instead; hanzo.id
21
+ * answers at once for a browser already signed in there, so the reader lands in
22
+ * the workspace with nothing to press.
23
+ */
24
+ export declare const ENTRY = "https://hanzo.team/?signin";
25
+ /**
26
+ * Where a business signs up for Hanzo Team: hanzo.team's /start. Email or a
27
+ * provider at hanzo.id, then the team's name, invitations and seats, then the
28
+ * Team plan's checkout — on the workspace's own origin, which is where the new
29
+ * team's session has to live.
30
+ */
31
+ export declare const START = "https://hanzo.team/start";
32
+ /**
33
+ * The web app, at `/` for a signed-in reader, in two modes. Chat is the default.
34
+ * Dev is @hanzo/build's `<Builder>`, and what it shows rides ONE query, `?at=`,
35
+ * in the builder's own grammar (`route` in @hanzo/build): '' New, `sess_<id>` a
36
+ * run, `-/<screen>`, `-/settings[/<section>]`, `-/customize[/<tab>]`,
37
+ * `-/plans`, `<slug>` a project. A query and not a path, because a static
38
+ * export has no server to answer a path under `/`.
39
+ */
40
+ export declare const AT = "at";
41
+ /** `/` in Dev at an app address; '' is Dev's New, `/?at=`. The key is the mode. */
42
+ export declare const app: (path: string) => string;
43
+ /**
44
+ * Chat, the app's default mode: `/` is a new conversation and `?chat=<id>` one
45
+ * of the reader's. So the address says which mode the app is in — `at` for Dev,
46
+ * anything else Chat — and a reload keeps it.
47
+ */
48
+ export declare const TALK = "chat";
49
+ /** `/` in Chat, on a conversation, or `/` itself for a new one. */
50
+ export declare const talk: (id?: string | null) => string;
51
+ /**
52
+ * The door opened on one run or project: `?run=` or `?project=`, the query the
53
+ * door turns into `?at=` — and the one the console and shared links already
54
+ * carry. Only a value that is exactly a session id or a project slug is
55
+ * carried; anything else opens /dev itself, so no caller can make this name
56
+ * another address.
57
+ */
58
+ export declare const dev: (ref?: string | null) => string;
59
+ /**
60
+ * The workspace's rooms: every route under app/(app) the apex does not keep.
61
+ *
62
+ * Derived from the tree `scripts/sync-rooms.mjs` walks, so a room added under
63
+ * app/(app) lives on hanzo.team unless the apex names it above.
64
+ */
65
+ export declare const ROOMS: readonly string[];
66
+ /** The apex's hosts, where the app is served and the rooms are not. */
67
+ export declare const APEX: readonly string[];
68
+ /** Whether a host is the apex. */
69
+ export declare const apex: (hostname: string) => boolean;
70
+ /** Whether a host is the workspace's own, where every room asks for a paid plan. */
71
+ export declare const team: (hostname: string) => boolean;
72
+ /** Whether an address on this export, query and all, opens one of the workspace's rooms. */
73
+ export declare function room(address: string): boolean;
74
+ /**
75
+ * Where an address belongs when it is asked for on `hostname`: on a `guest`
76
+ * alias, the same address on hanzo.ai, because every room signs in and a guest
77
+ * host cannot; on the apex, the same room and query on the workspace; otherwise
78
+ * null. Home is the workspace's root, so /home?room=x lands on /?room=x.
79
+ */
80
+ export declare const away: (hostname: string, address: string) => string | null;
81
+ /** Where a sign-in started on a `guest` host is finished. */
82
+ export declare const SIGNIN = "https://hanzo.ai";
83
+ /**
84
+ * The Hanzo App, to get: the page with Download, Install CLI and Open in web.
85
+ * It is this export's /app, served as the root of hanzo.app (universe
86
+ * static-sites `hanzo-app`), so it is linked by that address and hanzo.ai/app
87
+ * forwards there.
88
+ */
89
+ export declare const GET = "https://hanzo.app";
90
+ /**
91
+ * The app, opened from another host: the chat at hanzo.ai/chat. A reader who
92
+ * is signed out is asked to sign in there, on hanzo.ai's own page, and lands
93
+ * back in the chat.
94
+ */
95
+ export declare const WEB = "https://hanzo.ai/chat";
96
+ /**
97
+ * Whether a host serves this export and signs nobody in: hanzo.app, whose root
98
+ * is the page above; every `<slug>.hanzo.app` alias the Sites plane serves it
99
+ * at; and hanzo.chat, whose root is /forward. hanzo.app is a namespace
100
+ * customers publish into and hanzo.chat is only an address for the chat, so no
101
+ * sign-in runs under either: every address that needs one is the same address
102
+ * on hanzo.ai.
103
+ */
104
+ export declare const guest: (hostname: string) => boolean;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Which host this export is read on, and what that host keeps.
3
+ *
4
+ * hanzo.ai is the site for a stranger and the app for a member: the web app is
5
+ * `/` once signed in (app/page.tsx), beside /chat. hanzo.team is the
6
+ * workspace, its own app (hanzo-apps/team) with Home at its root. This export
7
+ * still carries the rooms, so a room asked for on the apex is sent to the same
8
+ * room on hanzo.team, and `output: 'export'` has no server to do that, so it is
9
+ * decided in the browser off the hostname, here.
10
+ *
11
+ * Two predicates, one per product: `apex()` for the app, `team()` for the
12
+ * workspace. Every host that is neither (localhost, a preview) draws the whole
13
+ * workspace and admits the way the apex does.
14
+ */
15
+ import APP from './rooms.json' with { type: 'json' };
16
+ import { site } from '../where.js';
17
+ /** The workspace's own address. */
18
+ export const WORKSPACE = 'https://hanzo.team';
19
+ /**
20
+ * The workspace, signed in. hanzo.team keeps its own session (its own origin's
21
+ * storage), so a reader signed in here arrives there signed out and is shown the
22
+ * landing. `?signin` asks it to start the sign-in on arrival instead; hanzo.id
23
+ * answers at once for a browser already signed in there, so the reader lands in
24
+ * the workspace with nothing to press.
25
+ */
26
+ export const ENTRY = `${WORKSPACE}/?signin`;
27
+ /**
28
+ * Where a business signs up for Hanzo Team: hanzo.team's /start. Email or a
29
+ * provider at hanzo.id, then the team's name, invitations and seats, then the
30
+ * Team plan's checkout — on the workspace's own origin, which is where the new
31
+ * team's session has to live.
32
+ */
33
+ export const START = `${WORKSPACE}/start`;
34
+ /**
35
+ * The web app, at `/` for a signed-in reader, in two modes. Chat is the default.
36
+ * Dev is @hanzo/build's `<Builder>`, and what it shows rides ONE query, `?at=`,
37
+ * in the builder's own grammar (`route` in @hanzo/build): '' New, `sess_<id>` a
38
+ * run, `-/<screen>`, `-/settings[/<section>]`, `-/customize[/<tab>]`,
39
+ * `-/plans`, `<slug>` a project. A query and not a path, because a static
40
+ * export has no server to answer a path under `/`.
41
+ */
42
+ export const AT = 'at';
43
+ /** `/` in Dev at an app address; '' is Dev's New, `/?at=`. The key is the mode. */
44
+ export const app = (path) => `/?${AT}=${path}`;
45
+ /**
46
+ * Chat, the app's default mode: `/` is a new conversation and `?chat=<id>` one
47
+ * of the reader's. So the address says which mode the app is in — `at` for Dev,
48
+ * anything else Chat — and a reload keeps it.
49
+ */
50
+ export const TALK = 'chat';
51
+ /** `/` in Chat, on a conversation, or `/` itself for a new one. */
52
+ export const talk = (id) => (id ? `/?${TALK}=${encodeURIComponent(id)}` : '/');
53
+ /**
54
+ * The door into the app: every "Try Hanzo Dev" link, the rail's Dev, the
55
+ * console's "open in the builder", dev.hanzo.ai. It signs a stranger in and
56
+ * forwards a member to Dev in the app, `/?at=` (app/(app)/dev/_door.tsx). It is
57
+ * the app's page, so it is reached through `site()`: here on hanzo.ai, across
58
+ * to hanzo.ai from hanzo.team.
59
+ */
60
+ const DEV = '/dev';
61
+ /** A run, as the platform mints its id. */
62
+ const SESSION = /^sess_[0-9a-f]{32}$/;
63
+ /** A project, as the Sites plane slugs it. */
64
+ const SLUG = /^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
65
+ /**
66
+ * The door opened on one run or project: `?run=` or `?project=`, the query the
67
+ * door turns into `?at=` — and the one the console and shared links already
68
+ * carry. Only a value that is exactly a session id or a project slug is
69
+ * carried; anything else opens /dev itself, so no caller can make this name
70
+ * another address.
71
+ */
72
+ export const dev = (ref) => site(!ref ? DEV : SESSION.test(ref) ? `${DEV}?run=${ref}` : SLUG.test(ref) ? `${DEV}?project=${ref}` : DEV);
73
+ /** The routes under app/(app) the apex keeps: the app — /chat, a chat shared by link, and /dev — and three surfaces that are not the workspace's. */
74
+ const KEPT = new Set(['/chat', '/chat/shared', '/dev', '/vibe', '/beluga', '/dashboard']);
75
+ /** Settings, and every page under it: the person's and the organization's, served wherever the app is. */
76
+ const SETTINGS = '/settings';
77
+ /**
78
+ * The workspace's rooms: every route under app/(app) the apex does not keep.
79
+ *
80
+ * Derived from the tree `scripts/sync-rooms.mjs` walks, so a room added under
81
+ * app/(app) lives on hanzo.team unless the apex names it above.
82
+ */
83
+ export const ROOMS = APP.filter((path) => !KEPT.has(path) && path !== SETTINGS && !path.startsWith(`${SETTINGS}/`));
84
+ /** The apex's hosts, where the app is served and the rooms are not. */
85
+ export const APEX = ['hanzo.ai', 'www.hanzo.ai'];
86
+ /** Whether a host is the apex. */
87
+ export const apex = (hostname) => APEX.includes(hostname);
88
+ /** Whether a host is the workspace's own, where every room asks for a paid plan. */
89
+ export const team = (hostname) => hostname === 'hanzo.team' || hostname === 'www.hanzo.team';
90
+ /** Whether an address on this export, query and all, opens one of the workspace's rooms. */
91
+ export function room(address) {
92
+ const path = address.split(/[?#]/)[0].replace(/\/+$/, '') || '/';
93
+ return ROOMS.includes(path);
94
+ }
95
+ /**
96
+ * Where an address belongs when it is asked for on `hostname`: on a `guest`
97
+ * alias, the same address on hanzo.ai, because every room signs in and a guest
98
+ * host cannot; on the apex, the same room and query on the workspace; otherwise
99
+ * null. Home is the workspace's root, so /home?room=x lands on /?room=x.
100
+ */
101
+ export const away = (hostname, address) => {
102
+ if (guest(hostname))
103
+ return SIGNIN + address;
104
+ if (!apex(hostname) || !room(address))
105
+ return null;
106
+ const home = address.match(/^\/home\/?(?=[?#]|$)/);
107
+ return home ? `${WORKSPACE}/${address.slice(home[0].length)}` : `${WORKSPACE}${address}`;
108
+ };
109
+ /** Where a sign-in started on a `guest` host is finished. */
110
+ export const SIGNIN = 'https://hanzo.ai';
111
+ /**
112
+ * The Hanzo App, to get: the page with Download, Install CLI and Open in web.
113
+ * It is this export's /app, served as the root of hanzo.app (universe
114
+ * static-sites `hanzo-app`), so it is linked by that address and hanzo.ai/app
115
+ * forwards there.
116
+ */
117
+ export const GET = 'https://hanzo.app';
118
+ /**
119
+ * The app, opened from another host: the chat at hanzo.ai/chat. A reader who
120
+ * is signed out is asked to sign in there, on hanzo.ai's own page, and lands
121
+ * back in the chat.
122
+ */
123
+ export const WEB = `${SIGNIN}/chat`;
124
+ /**
125
+ * Whether a host serves this export and signs nobody in: hanzo.app, whose root
126
+ * is the page above; every `<slug>.hanzo.app` alias the Sites plane serves it
127
+ * at; and hanzo.chat, whose root is /forward. hanzo.app is a namespace
128
+ * customers publish into and hanzo.chat is only an address for the chat, so no
129
+ * sign-in runs under either: every address that needs one is the same address
130
+ * on hanzo.ai.
131
+ */
132
+ export const guest = (hostname) => hostname === 'hanzo.app' || hostname.endsWith('.hanzo.app') || hostname === 'hanzo.chat';
@@ -0,0 +1 @@
1
+ export declare function useHost(): string;
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { useSyncExternalStore } from 'react';
3
+ /**
4
+ * This page's hostname once React owns it, and '' in the render that hydrates.
5
+ *
6
+ * The export is prerendered with no host at all, so the server's answer is the
7
+ * empty string and hydration's first client render agrees with it; the next
8
+ * render has the real one. `useHydrated`'s shape, for the same reason: nothing
9
+ * subscribes, because a page's host never changes under it.
10
+ */
11
+ const NEVER = () => () => { };
12
+ export function useHost() {
13
+ return useSyncExternalStore(NEVER, () => window.location.hostname, () => '');
14
+ }
@@ -0,0 +1 @@
1
+ export declare function useHydrated(): boolean;
@@ -0,0 +1,20 @@
1
+ 'use client';
2
+ import { useSyncExternalStore } from 'react';
3
+ /**
4
+ * False while the server's markup is on screen, true once React owns it.
5
+ *
6
+ * Three places needed this and each wrote it as `useState(false)` plus
7
+ * `useEffect(() => setReady(true), [])` — the shape
8
+ * `react-hooks/set-state-in-effect` names, and a second render on every mount.
9
+ *
10
+ * `useSyncExternalStore` says it without the copy: the server snapshot is
11
+ * `false`, so the SSR'd markup and the first client paint agree and hydration
12
+ * matches; the client snapshot is `true`, so the very next render — the one
13
+ * hydration already performs — has the real answer. Nothing subscribes, because
14
+ * nothing changes after that, which is why `subscribe` returns an unsubscribe
15
+ * that does nothing and is defined at module scope so its identity never moves.
16
+ */
17
+ const NEVER = () => () => { };
18
+ export function useHydrated() {
19
+ return useSyncExternalStore(NEVER, () => true, () => false);
20
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * An SDK, framework or platform the host publishes a guide for. The catalogue
3
+ * is the host's content (hanzo.ai's /integrations) and arrives through
4
+ * `configure({ integrations })`; Directory lists it as the apps a team connects.
5
+ */
6
+ export interface Integration {
7
+ slug: string;
8
+ name: string;
9
+ description: string;
10
+ category: 'sdk' | 'framework' | 'platform' | 'infra' | 'language';
11
+ icon: string;
12
+ examples: {
13
+ lang: string;
14
+ label: string;
15
+ code: string;
16
+ }[];
17
+ upstream: string;
18
+ creator: string;
19
+ creatorUrl: string;
20
+ license: string;
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ export declare function markSignupIntent(): void;
2
+ export declare function markMethod(method: string): void;
3
+ export declare function signinMethod(): string;
4
+ /** Read-once: returns true when this callback completes a signup, and clears. */
5
+ export declare function takeSignupIntent(): boolean;
6
+ export declare function countSignup(method?: string): void;
@@ -0,0 +1,71 @@
1
+ import { track } from './tags.js';
2
+ /**
3
+ * Signup vs login, decided in ONE place.
4
+ *
5
+ * IAM hosts the form, so /auth/callback sees the same redirect for a brand-new
6
+ * account and a returning sign-in — and counting a returning login as a signup
7
+ * would silently inflate the top-line conversion. /signup marks the intent
8
+ * before handing off; the callback takes it (read-once) and picks the event.
9
+ *
10
+ * sessionStorage, not localStorage: the intent must not survive the tab.
11
+ */
12
+ const KEY = 'hz_signup_intent';
13
+ export function markSignupIntent() {
14
+ try {
15
+ window.sessionStorage.setItem(KEY, '1');
16
+ }
17
+ catch {
18
+ /* private mode — the callback then reports login_completed; never throw */
19
+ }
20
+ }
21
+ /**
22
+ * How this tab is signing in, written when the person commits to a way:
23
+ * `email` (a new account), `password`, `code`, a provider's kind (`google`,
24
+ * `github`), `hanzo.id` (IAM's own page), or `session` (a hanzo.id session,
25
+ * asked silently). The callback's login and the first organization's sign-up
26
+ * carry it as `method`; a sign-in that began nowhere on this site is `hanzo.id`.
27
+ */
28
+ const METHOD = 'hz_signin_method';
29
+ export function markMethod(method) {
30
+ try {
31
+ window.sessionStorage.setItem(METHOD, method);
32
+ }
33
+ catch {
34
+ /* private mode — the method reads as hanzo.id; never throw */
35
+ }
36
+ }
37
+ export function signinMethod() {
38
+ try {
39
+ return window.sessionStorage.getItem(METHOD) || 'hanzo.id';
40
+ }
41
+ catch {
42
+ return 'hanzo.id';
43
+ }
44
+ }
45
+ /** Read-once: returns true when this callback completes a signup, and clears. */
46
+ export function takeSignupIntent() {
47
+ try {
48
+ const had = window.sessionStorage.getItem(KEY) === '1';
49
+ window.sessionStorage.removeItem(KEY);
50
+ return had;
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ /**
57
+ * The account was created: signup_completed, once per tab however many places
58
+ * could say so (the terms page's Create account, the first organization).
59
+ */
60
+ const COUNTED = 'hz_signup_counted';
61
+ export function countSignup(method = signinMethod()) {
62
+ try {
63
+ if (window.sessionStorage.getItem(COUNTED))
64
+ return;
65
+ window.sessionStorage.setItem(COUNTED, '1');
66
+ }
67
+ catch {
68
+ /* private mode — counted on every call rather than never */
69
+ }
70
+ track('signup_completed', { method });
71
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Organization invitations, on the real IAM surface.
3
+ *
4
+ * The contract, source-verified end to end:
5
+ * - create POST /v1/iam/invitations (schema iam.invitations.Input)
6
+ * - list GET /v1/iam/invitations?owner=<org> → { invitations, total }
7
+ * - withdraw DELETE /v1/iam/invitations/<owner>/<name>
8
+ * - send POST /v1/iam/invitations/<owner>/<name>/send
9
+ * {} → { sent, to }: IAM emails the join link to the address the
10
+ * invitation is pinned to, from the org of the application the
11
+ * caller's access token was issued to (hanzo-app here).
12
+ * - redeem https://hanzo.id/join?client_id=<CLIENT>&invite=<CODE>&org=<owner>
13
+ * — the link IAM emails, built here identically. A new person
14
+ * creates their account there with the code, and a signed-in one
15
+ * accepts it; the server model (iam pkg/schema/invitation.go)
16
+ * gates on State "Active", counts UsedCount against Quota, and an
17
+ * Email pin constrains who may redeem.
18
+ *
19
+ * A refusal states its reason. 401 is the credential, 403 is permission —
20
+ * neither is "try again", so neither ever says it.
21
+ */
22
+ export interface Invitation {
23
+ owner: string;
24
+ name: string;
25
+ displayName: string;
26
+ code: string;
27
+ quota: number;
28
+ usedCount: number;
29
+ email?: string;
30
+ state: string;
31
+ createdTime?: string;
32
+ }
33
+ export declare class InviteRefusal extends Error {
34
+ readonly status: number;
35
+ constructor(status: number, message: string);
36
+ }
37
+ /** The link a member joins by, on the same IAM the account panel signs into.
38
+ * Those two were separately configurable and neither name was set anywhere, so
39
+ * pointing the app at another IAM would have moved one and left the other. It is
40
+ * the link IAM emails, parameter for parameter, so a copied link and an emailed
41
+ * one are the same link: the application the member joins through, the code, and
42
+ * the org IAM looks the code up in. */
43
+ export declare function inviteLink(org: string, code: string): string;
44
+ /** How many people one shared invite link admits. */
45
+ export declare const LINK_SEATS = 25;
46
+ /**
47
+ * Issues one invitation and returns it: pinned to an email and redeemable once,
48
+ * or open to anyone with the link for `quota` joins. The caller owns telling the
49
+ * person — `sendInvitation` emails a pinned one, and the link is
50
+ * `inviteLink(invitation.owner, invitation.code)`.
51
+ */
52
+ export declare function createInvitation(org: string, email?: string, quota?: number): Promise<Invitation>;
53
+ /**
54
+ * One organization's invitations, newest first, redeemed ones included.
55
+ *
56
+ * The organization is named as `?owner=`, the one input IAM's list reads. A
57
+ * read that names none is authorized against no organization and refused.
58
+ */
59
+ export declare function listInvitations(org: string): Promise<Invitation[]>;
60
+ /**
61
+ * Emails the join link to the address an invitation is pinned to, and returns
62
+ * the address IAM sent it to. A refusal carries IAM's own reason.
63
+ */
64
+ export declare function sendInvitation(inv: Pick<Invitation, 'owner' | 'name'>): Promise<string>;
65
+ /**
66
+ * The organization's shared invite link: an active invitation pinned to nobody
67
+ * with a seat left. Pinned invitations are one person's and are not it.
68
+ */
69
+ export declare function shareable(rows: Invitation[]): Invitation | undefined;
70
+ /** Withdraws an invitation so its code stops redeeming. */
71
+ export declare function withdrawInvitation(inv: Pick<Invitation, 'owner' | 'name'>): Promise<void>;
72
+ /**
73
+ * Sets how the organization APPEARS: an image, or one emoji, or neither.
74
+ *
75
+ * `POST /v1/iam/organizations/avatar` writes those two fields onto the stored
76
+ * row and touches nothing else — which the general update cannot do, because it
77
+ * replaces the whole record and a record read back arrives MASKED, so a
78
+ * read-modify-write through it would persist the mask over the organization's
79
+ * own credential settings. That is IAM's own reason for publishing this route,
80
+ * and it is why the logo does not go through `updateOrganization`.
81
+ *
82
+ * An image is an https link or the bytes inline as a data URL, up to 96 KiB —
83
+ * the same bound and the same shape `lib/mark.ts` already produces for a
84
+ * person's photograph, so a logo is not a second kind of picture.
85
+ */
86
+ export declare function setOrganizationMark(owner: string, name: string, mark: {
87
+ avatar?: string;
88
+ emoji?: string;
89
+ }): Promise<void>;
90
+ /**
91
+ * Changes how an organization READS: its display name and its website.
92
+ *
93
+ * `POST /v1/iam/organizations/profile`, the sibling of the avatar route, and it
94
+ * exists for the same reason. Update REPLACES the record, so sending just a new
95
+ * name blanks the website and the sign-in rules — and a record read back arrives
96
+ * MASKED, so the obvious read-modify-write stores "***" as the master password.
97
+ * One costs the organization its settings, the other its credentials. This
98
+ * writes the fields it names and touches nothing else.
99
+ *
100
+ * A field left `undefined` is not sent and not changed; `''` is sent and clears.
101
+ */
102
+ export declare function setOrganizationProfile(owner: string, name: string, profile: {
103
+ displayName?: string;
104
+ websiteUrl?: string;
105
+ }): Promise<void>;
106
+ /** One person in the organization. */
107
+ export interface Member {
108
+ /** "<homeOrg>/<username>", the membership's key. */
109
+ id: string;
110
+ name: string;
111
+ email: string;
112
+ /** The role IAM gives them here: owner | admin | member. */
113
+ role: string;
114
+ /**
115
+ * Their account LIVES in this org, so their access is the account's own and
116
+ * not a membership row: IAM refuses to revoke it (the account is disabled
117
+ * instead) and reads their role off the account (store.HomeRole).
118
+ */
119
+ home: boolean;
120
+ }
121
+ /**
122
+ * The organization's people: everyone who may act in it, with their role.
123
+ *
124
+ * Two reads, because IAM grants access two ways: a MEMBERSHIP row (someone who
125
+ * lives in another org and was let into this one) and an account that LIVES in
126
+ * this org, whose access is the account itself and has no row. A person usually
127
+ * lives in their own personal organization and acts in a team one, so a team
128
+ * org's people are mostly rows; an older org's people mostly live in it.
129
+ *
130
+ * A membership row carries the account and the role and no name, and IAM lets a
131
+ * reader open accounts in the org's own directory and no other org's. So each
132
+ * member is named from what the reader may read: the reader from their own
133
+ * account (`self`, where the caller holds it), a member who lives in the org
134
+ * from its directory, and anyone else by the username the row carries. The
135
+ * directory also marks the org's service accounts, which are left out: the
136
+ * roster beside the agents is where somebody looks for those. A reader who may
137
+ * not read the directory sees the rows alone, by username.
138
+ */
139
+ export declare function members(org: string, self?: {
140
+ name: string;
141
+ email: string;
142
+ }): Promise<Member[]>;
143
+ /**
144
+ * Changes an existing member's role: `POST /v1/iam/memberships {user, org, role}`,
145
+ * the one role write HIP-0527 names, by an owner or admin of the org. Resolves to
146
+ * the role IAM holds for them afterwards, read back from the roster, so a caller
147
+ * reports what IAM stored rather than what it asked for.
148
+ */
149
+ export declare function setRole(org: string, user: string, next: string): Promise<string | null>;
150
+ /**
151
+ * Ends someone's access to the org: `POST /v1/iam/delete-membership {user, org}`.
152
+ * Their account survives. IAM refuses it for a person whose account lives in the
153
+ * org, and says so in its own words.
154
+ */
155
+ export declare function removeMember(org: string, user: string): Promise<void>;