@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.
- package/LICENSE-APACHE +205 -0
- package/LICENSE-MIT +21 -0
- package/README.md +23 -0
- package/dist/Account.d.ts +8 -0
- package/dist/Account.js +58 -0
- package/dist/Beluga.d.ts +12 -0
- package/dist/Beluga.js +152 -0
- package/dist/Billing.d.ts +1 -0
- package/dist/Billing.js +31 -0
- package/dist/Board.d.ts +9 -0
- package/dist/Board.js +447 -0
- package/dist/Boxes.d.ts +1 -0
- package/dist/Boxes.js +173 -0
- package/dist/CalScheduler.d.ts +15 -0
- package/dist/CalScheduler.js +196 -0
- package/dist/Chat.d.ts +35 -0
- package/dist/Chat.js +1049 -0
- package/dist/Column.d.ts +15 -0
- package/dist/Column.js +88 -0
- package/dist/Contacts.d.ts +1 -0
- package/dist/Contacts.js +301 -0
- package/dist/Directory.d.ts +24 -0
- package/dist/Directory.js +356 -0
- package/dist/Drive.d.ts +2 -0
- package/dist/Drive.js +466 -0
- package/dist/Find.d.ts +7 -0
- package/dist/Find.js +82 -0
- package/dist/Guide.d.ts +6 -0
- package/dist/Guide.js +272 -0
- package/dist/Home.d.ts +39 -0
- package/dist/Home.js +306 -0
- package/dist/Inbox.d.ts +9 -0
- package/dist/Inbox.js +193 -0
- package/dist/Market.d.ts +3 -0
- package/dist/Market.js +149 -0
- package/dist/Me.d.ts +27 -0
- package/dist/Me.js +251 -0
- package/dist/Meet.d.ts +9 -0
- package/dist/Meet.js +370 -0
- package/dist/ModeMark.d.ts +8 -0
- package/dist/ModeMark.js +18 -0
- package/dist/OrgSwitcher.d.ts +31 -0
- package/dist/OrgSwitcher.js +120 -0
- package/dist/Orgs.d.ts +10 -0
- package/dist/Orgs.js +582 -0
- package/dist/Profile.d.ts +4 -0
- package/dist/Profile.js +67 -0
- package/dist/Prose.d.ts +5 -0
- package/dist/Prose.js +111 -0
- package/dist/ProviderMark.d.ts +17 -0
- package/dist/ProviderMark.js +277 -0
- package/dist/Recents.d.ts +10 -0
- package/dist/Recents.js +91 -0
- package/dist/RightPane.d.ts +13 -0
- package/dist/RightPane.js +426 -0
- package/dist/Room.d.ts +8 -0
- package/dist/Room.js +50 -0
- package/dist/Scene.d.ts +12 -0
- package/dist/Scene.js +105 -0
- package/dist/Settings.d.ts +46 -0
- package/dist/Settings.js +321 -0
- package/dist/Setup.d.ts +1 -0
- package/dist/Setup.js +36 -0
- package/dist/Share.d.ts +3 -0
- package/dist/Share.js +130 -0
- package/dist/Shared.d.ts +1 -0
- package/dist/Shared.js +113 -0
- package/dist/Shell.d.ts +63 -0
- package/dist/Shell.js +1225 -0
- package/dist/Start.d.ts +1 -0
- package/dist/Start.js +96 -0
- package/dist/Support.d.ts +14 -0
- package/dist/Support.js +122 -0
- package/dist/Talk.d.ts +4 -0
- package/dist/Talk.js +107 -0
- package/dist/Upgrade.d.ts +11 -0
- package/dist/Upgrade.js +75 -0
- package/dist/Usage.d.ts +1 -0
- package/dist/Usage.js +21 -0
- package/dist/Vibe.d.ts +2 -0
- package/dist/Vibe.js +188 -0
- package/dist/Work.d.ts +12 -0
- package/dist/Work.js +256 -0
- package/dist/bots/Bots.d.ts +2 -0
- package/dist/bots/Bots.js +456 -0
- package/dist/bots/Lights.d.ts +8 -0
- package/dist/bots/Lights.js +13 -0
- package/dist/bots/api.d.ts +120 -0
- package/dist/bots/api.js +75 -0
- package/dist/bots/face.d.ts +5 -0
- package/dist/bots/face.js +13 -0
- package/dist/bots/ink.d.ts +7 -0
- package/dist/bots/ink.js +29 -0
- package/dist/bots/kit.d.ts +113 -0
- package/dist/bots/kit.js +43 -0
- package/dist/bots/runtime.d.ts +4 -0
- package/dist/bots/runtime.js +326 -0
- package/dist/card.d.ts +52 -0
- package/dist/card.js +52 -0
- package/dist/cast.d.ts +143 -0
- package/dist/cast.js +251 -0
- package/dist/connect.d.ts +29 -0
- package/dist/connect.js +64 -0
- package/dist/contacts-store.d.ts +111 -0
- package/dist/contacts-store.js +276 -0
- package/dist/conversations.d.ts +54 -0
- package/dist/conversations.js +98 -0
- package/dist/copy.d.ts +23 -0
- package/dist/copy.js +39 -0
- package/dist/crew.d.ts +36 -0
- package/dist/crew.js +304 -0
- package/dist/dev.d.ts +3 -0
- package/dist/dev.js +102 -0
- package/dist/enso.d.ts +70 -0
- package/dist/enso.js +112 -0
- package/dist/failure.d.ts +57 -0
- package/dist/failure.js +143 -0
- package/dist/ground.d.ts +32 -0
- package/dist/ground.js +32 -0
- package/dist/host.d.ts +76 -0
- package/dist/host.js +76 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +12 -0
- package/dist/kept.d.ts +5 -0
- package/dist/kept.js +27 -0
- package/dist/lib/account.d.ts +49 -0
- package/dist/lib/account.js +229 -0
- package/dist/lib/ai.d.ts +46 -0
- package/dist/lib/ai.js +140 -0
- package/dist/lib/api.d.ts +43 -0
- package/dist/lib/api.js +62 -0
- package/dist/lib/bare.d.ts +9 -0
- package/dist/lib/bare.js +7 -0
- package/dist/lib/coding.d.ts +105 -0
- package/dist/lib/coding.js +87 -0
- package/dist/lib/destination.d.ts +102 -0
- package/dist/lib/destination.js +260 -0
- package/dist/lib/durable.d.ts +66 -0
- package/dist/lib/durable.js +107 -0
- package/dist/lib/first.d.ts +11 -0
- package/dist/lib/first.js +23 -0
- package/dist/lib/home.d.ts +61 -0
- package/dist/lib/home.js +54 -0
- package/dist/lib/host.d.ts +104 -0
- package/dist/lib/host.js +132 -0
- package/dist/lib/hostname.d.ts +1 -0
- package/dist/lib/hostname.js +14 -0
- package/dist/lib/hydrated.d.ts +1 -0
- package/dist/lib/hydrated.js +20 -0
- package/dist/lib/integrations.d.ts +21 -0
- package/dist/lib/integrations.js +1 -0
- package/dist/lib/intent.d.ts +6 -0
- package/dist/lib/intent.js +71 -0
- package/dist/lib/invite.d.ts +155 -0
- package/dist/lib/invite.js +245 -0
- package/dist/lib/limits.d.ts +55 -0
- package/dist/lib/limits.js +119 -0
- package/dist/lib/list.d.ts +9 -0
- package/dist/lib/list.js +11 -0
- package/dist/lib/mark.d.ts +13 -0
- package/dist/lib/mark.js +85 -0
- package/dist/lib/mix.d.ts +22 -0
- package/dist/lib/mix.js +22 -0
- package/dist/lib/models.d.ts +27 -0
- package/dist/lib/models.js +106 -0
- package/dist/lib/org.d.ts +109 -0
- package/dist/lib/org.js +140 -0
- package/dist/lib/path.d.ts +9 -0
- package/dist/lib/path.js +29 -0
- package/dist/lib/plans.d.ts +193 -0
- package/dist/lib/plans.js +310 -0
- package/dist/lib/prose.d.ts +13 -0
- package/dist/lib/prose.js +62 -0
- package/dist/lib/reach.d.ts +17 -0
- package/dist/lib/reach.js +70 -0
- package/dist/lib/rooms.json +23 -0
- package/dist/lib/served.d.ts +20 -0
- package/dist/lib/served.js +92 -0
- package/dist/lib/session.d.ts +211 -0
- package/dist/lib/session.js +493 -0
- package/dist/lib/share.d.ts +131 -0
- package/dist/lib/share.js +216 -0
- package/dist/lib/signout.d.ts +14 -0
- package/dist/lib/signout.js +40 -0
- package/dist/lib/tags.d.ts +27 -0
- package/dist/lib/tags.js +46 -0
- package/dist/lib/tap.d.ts +17 -0
- package/dist/lib/tap.js +9 -0
- package/dist/lib/tier.d.ts +115 -0
- package/dist/lib/tier.js +207 -0
- package/dist/lib/tiers.d.ts +9 -0
- package/dist/lib/tiers.js +141 -0
- package/dist/lib/todo.d.ts +125 -0
- package/dist/lib/todo.js +379 -0
- package/dist/look.d.ts +1 -0
- package/dist/look.js +21 -0
- package/dist/meters.d.ts +8 -0
- package/dist/meters.js +31 -0
- package/dist/model.d.ts +4 -0
- package/dist/model.js +44 -0
- package/dist/network.d.ts +64 -0
- package/dist/network.js +216 -0
- package/dist/open.d.ts +81 -0
- package/dist/open.js +149 -0
- package/dist/pane.d.ts +92 -0
- package/dist/pane.js +231 -0
- package/dist/progress.d.ts +46 -0
- package/dist/progress.js +51 -0
- package/dist/recent.d.ts +67 -0
- package/dist/recent.js +161 -0
- package/dist/rooms.d.ts +131 -0
- package/dist/rooms.js +195 -0
- package/dist/roster.d.ts +28 -0
- package/dist/roster.js +70 -0
- package/dist/span.d.ts +14 -0
- package/dist/span.js +50 -0
- package/dist/speech.d.ts +44 -0
- package/dist/speech.js +229 -0
- package/dist/stars.d.ts +16 -0
- package/dist/stars.js +75 -0
- package/dist/tabs.d.ts +26 -0
- package/dist/tabs.js +25 -0
- package/dist/team.d.ts +11 -0
- package/dist/team.gen.d.ts +10 -0
- package/dist/team.gen.js +11 -0
- package/dist/team.js +30 -0
- package/dist/tools.d.ts +35 -0
- package/dist/tools.js +143 -0
- package/dist/tours.d.ts +24 -0
- package/dist/tours.js +311 -0
- package/dist/waiting.d.ts +15 -0
- package/dist/waiting.js +17 -0
- package/dist/where.d.ts +54 -0
- package/dist/where.js +14 -0
- package/package.json +108 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a provider and a model are CALLED — the few naming rules the rooms draw
|
|
3
|
+
* with. The catalogue itself (prices, contexts, the serving list) is the host's.
|
|
4
|
+
*/
|
|
5
|
+
import { getOrgAndSlug } from './path.js';
|
|
6
|
+
export { canonicalOrg, getOrgAndSlug, modelPagePath } from './path.js';
|
|
7
|
+
// Map org slug → display name
|
|
8
|
+
export const ORG_DISPLAY_NAMES = {
|
|
9
|
+
hanzo: 'Hanzo',
|
|
10
|
+
openai: 'OpenAI',
|
|
11
|
+
anthropic: 'Anthropic',
|
|
12
|
+
google: 'Google',
|
|
13
|
+
'meta-llama': 'Meta',
|
|
14
|
+
'x-ai': 'xAI',
|
|
15
|
+
mistralai: 'Mistral',
|
|
16
|
+
deepseek: 'DeepSeek',
|
|
17
|
+
qwen: 'Qwen',
|
|
18
|
+
nvidia: 'NVIDIA',
|
|
19
|
+
'z-ai': 'Z.ai',
|
|
20
|
+
'arcee-ai': 'Arcee AI',
|
|
21
|
+
minimax: 'Minimax',
|
|
22
|
+
allenai: 'Allen AI',
|
|
23
|
+
nousresearch: 'Nous Research',
|
|
24
|
+
liquid: 'Liquid AI',
|
|
25
|
+
moonshotai: 'Moonshot AI',
|
|
26
|
+
amazon: 'Amazon',
|
|
27
|
+
perplexity: 'Perplexity',
|
|
28
|
+
baidu: 'Baidu',
|
|
29
|
+
cohere: 'Cohere',
|
|
30
|
+
'bytedance-seed': 'ByteDance Seed',
|
|
31
|
+
openrouter: 'OpenRouter',
|
|
32
|
+
microsoft: 'Microsoft',
|
|
33
|
+
inflection: 'Inflection',
|
|
34
|
+
sao10k: 'Sao10K',
|
|
35
|
+
'aion-labs': 'Aion Labs',
|
|
36
|
+
thedrummer: 'TheDrummer',
|
|
37
|
+
stepfun: 'StepFun',
|
|
38
|
+
relace: 'Relace',
|
|
39
|
+
morph: 'Morph',
|
|
40
|
+
inception: 'Inception',
|
|
41
|
+
neversleep: 'NeverSleep',
|
|
42
|
+
upstage: 'Upstage',
|
|
43
|
+
writer: 'Writer',
|
|
44
|
+
xiaomi: 'Xiaomi',
|
|
45
|
+
'nex-agi': 'Nex-AGI',
|
|
46
|
+
essentialai: 'EssentialAI',
|
|
47
|
+
'prime-intellect': 'Prime Intellect',
|
|
48
|
+
deepcogito: 'DeepCogito',
|
|
49
|
+
kwaipilot: 'KwaiPilot',
|
|
50
|
+
'ibm-granite': 'IBM Granite',
|
|
51
|
+
alibaba: 'Alibaba',
|
|
52
|
+
opengvlab: 'OpenGVLab',
|
|
53
|
+
meituan: 'Meituan',
|
|
54
|
+
ai21: 'AI21',
|
|
55
|
+
bytedance: 'ByteDance',
|
|
56
|
+
switchpoint: 'Switchpoint',
|
|
57
|
+
cognitivecomputations: 'Cognitive Computations',
|
|
58
|
+
tencent: 'Tencent',
|
|
59
|
+
tngtech: 'TNG Tech',
|
|
60
|
+
eleutherai: 'EleutherAI',
|
|
61
|
+
alfredpros: 'AlfredPros',
|
|
62
|
+
raifle: 'Raifle',
|
|
63
|
+
'anthracite-org': 'Anthracite',
|
|
64
|
+
alpindale: 'Alpindale',
|
|
65
|
+
mancer: 'Mancer',
|
|
66
|
+
undi95: 'Undi95',
|
|
67
|
+
gryphe: 'Gryphe',
|
|
68
|
+
meta: 'Meta',
|
|
69
|
+
inclusionai: 'inclusionAI',
|
|
70
|
+
thinkingmachines: 'Thinking Machines',
|
|
71
|
+
rekaai: 'Reka AI',
|
|
72
|
+
sakana: 'Sakana AI',
|
|
73
|
+
perceptron: 'Perceptron',
|
|
74
|
+
};
|
|
75
|
+
export function orgDisplayName(org) {
|
|
76
|
+
if (!org)
|
|
77
|
+
return 'Hanzo';
|
|
78
|
+
return ORG_DISPLAY_NAMES[org] ?? org;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* What a model is CALLED, as against the id it answers to.
|
|
82
|
+
*
|
|
83
|
+
* Two things the catalog does that a reader should not have to:
|
|
84
|
+
*
|
|
85
|
+
* it names the house families by their slug — `name` is `"enso"`, the same
|
|
86
|
+
* string as `id` — so the card would read "enso" twice, once in prose type
|
|
87
|
+
* and once in mono. Title-case the slug and the card reads "Enso Flash" over
|
|
88
|
+
* `enso-flash`, which is a name over an address.
|
|
89
|
+
*
|
|
90
|
+
* it prefixes third-party names with the lab — `"DeepSeek: DeepSeek V4 Pro"`,
|
|
91
|
+
* `"SpaceXAI: Grok 4.5"`. The lab is already the group the row sits in and
|
|
92
|
+
* the namespace on the id beside it, so the prefix is the third telling.
|
|
93
|
+
*
|
|
94
|
+
* It lived on /models as a private helper while the table, the tile and the lab
|
|
95
|
+
* index all needed it, which is how the same model could read three ways on one
|
|
96
|
+
* page.
|
|
97
|
+
*/
|
|
98
|
+
export function modelName(model) {
|
|
99
|
+
if (model.name && model.name !== model.id) {
|
|
100
|
+
return model.name.replace(/^[A-Za-z0-9.\-() ]{1,18}:\s+/, '');
|
|
101
|
+
}
|
|
102
|
+
return getOrgAndSlug(model.id)
|
|
103
|
+
.slug.split('-')
|
|
104
|
+
.map((word) => (word ? word[0].toUpperCase() + word.slice(1) : word))
|
|
105
|
+
.join(' ');
|
|
106
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The organization settings area's reads and writes, each on the API that owns it.
|
|
3
|
+
*
|
|
4
|
+
* the org row GET /v1/iam/organizations/admin/<org>
|
|
5
|
+
* delete the org DELETE /v1/iam/organizations/admin/<org>
|
|
6
|
+
* found a team org POST /v1/iam/organizations {owner: admin, name, displayName}
|
|
7
|
+
* business address GET|PUT /v1/settings/organization (cloud's per-org config)
|
|
8
|
+
* your keys GET|POST|DELETE /v1/account/keys (minted by IAM on you, shown once)
|
|
9
|
+
* the org's keys GET /v1/iam/keys?owner=<org>, DELETE /v1/iam/keys/<org>/<name>
|
|
10
|
+
* payment methods GET /v1/billing/methods
|
|
11
|
+
* invoices GET /v1/billing/invoices
|
|
12
|
+
*
|
|
13
|
+
* The name, the logo, the members and the invitations are lib/hanzo/team.ts's,
|
|
14
|
+
* and the plan and balance are lib/hanzo/tier.ts's. Nothing here decides who may
|
|
15
|
+
* do what: every request carries the reader's bearer and organization (`scope`),
|
|
16
|
+
* the server answers, and a refusal comes back as the server's own sentence.
|
|
17
|
+
*/
|
|
18
|
+
/** A refusal, with the status it came under and the server's words. */
|
|
19
|
+
export declare class Refusal extends Error {
|
|
20
|
+
readonly status: number;
|
|
21
|
+
constructor(status: number, message: string);
|
|
22
|
+
}
|
|
23
|
+
/** The words of any failure, for a toast or a line under a control. */
|
|
24
|
+
export declare const say: (e: unknown) => string;
|
|
25
|
+
/** The org row as IAM answers it. Every org row is filed under the reserved `admin` owner. */
|
|
26
|
+
export interface OrgRow {
|
|
27
|
+
name: string;
|
|
28
|
+
displayName: string;
|
|
29
|
+
websiteUrl?: string;
|
|
30
|
+
isPersonal?: boolean;
|
|
31
|
+
avatar?: string;
|
|
32
|
+
}
|
|
33
|
+
export declare const readOrg: (org: string) => Promise<OrgRow>;
|
|
34
|
+
/** Deletes the org row. IAM decides who may; there is no undo. */
|
|
35
|
+
export declare const deleteOrg: (org: string) => Promise<unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* Creates an organization the reader owns: cloud's `POST /v1/account/orgs
|
|
38
|
+
* {name}`, the one create every surface uses. Cloud files the row in IAM and
|
|
39
|
+
* records the verified creator as its owner through IAM's membership API.
|
|
40
|
+
* Answers the new organization's handle; a refusal is what the reader is shown.
|
|
41
|
+
*/
|
|
42
|
+
export declare const createOrg: (name: string) => Promise<string>;
|
|
43
|
+
/** The handle an org is addressed by, from the name a person typed. */
|
|
44
|
+
export declare function slug(name: string): string;
|
|
45
|
+
export interface Address {
|
|
46
|
+
line1: string;
|
|
47
|
+
line2: string;
|
|
48
|
+
city: string;
|
|
49
|
+
state: string;
|
|
50
|
+
postalCode: string;
|
|
51
|
+
country: string;
|
|
52
|
+
}
|
|
53
|
+
export declare const EMPTY_ADDRESS: Address;
|
|
54
|
+
export declare function readAddress(): Promise<Address>;
|
|
55
|
+
/**
|
|
56
|
+
* Stores the address and answers what was stored. The document is written
|
|
57
|
+
* whole, so the rest of it is read first and carried: a save of the address
|
|
58
|
+
* never drops a field somebody else kept beside it.
|
|
59
|
+
*/
|
|
60
|
+
export declare function saveAddress(next: Address): Promise<Address>;
|
|
61
|
+
export type KeyType = 'secret' | 'publishable';
|
|
62
|
+
/** One of the reader's own keys in the acting org. A secret key's value is never listed. */
|
|
63
|
+
export interface OwnKey {
|
|
64
|
+
type: KeyType;
|
|
65
|
+
prefix?: string;
|
|
66
|
+
/** The whole value, for a publishable key only: it is public by construction. */
|
|
67
|
+
key?: string;
|
|
68
|
+
createdAt?: string;
|
|
69
|
+
}
|
|
70
|
+
export declare function ownKeys(): Promise<OwnKey[]>;
|
|
71
|
+
/** Mints the reader's key of `type`, replacing the one they held, and answers it — once. */
|
|
72
|
+
export declare function mintKey(type: KeyType): Promise<string>;
|
|
73
|
+
export declare const revokeOwnKey: (type: KeyType) => Promise<unknown>;
|
|
74
|
+
/** One of the org's keys, as IAM lists them: the secret half never leaves IAM. */
|
|
75
|
+
export interface OrgKey {
|
|
76
|
+
owner: string;
|
|
77
|
+
name: string;
|
|
78
|
+
displayName?: string;
|
|
79
|
+
user?: string;
|
|
80
|
+
scope?: string;
|
|
81
|
+
state?: string;
|
|
82
|
+
createdTime?: string;
|
|
83
|
+
}
|
|
84
|
+
export declare function orgKeys(org: string): Promise<OrgKey[]>;
|
|
85
|
+
export declare const revokeKey: (k: Pick<OrgKey, 'owner' | 'name'>) => Promise<unknown>;
|
|
86
|
+
/** A card on file, as billing lists it. */
|
|
87
|
+
export interface Method {
|
|
88
|
+
id: string;
|
|
89
|
+
brand?: string;
|
|
90
|
+
last4?: string;
|
|
91
|
+
expMonth?: number;
|
|
92
|
+
expYear?: number;
|
|
93
|
+
isDefault?: boolean;
|
|
94
|
+
}
|
|
95
|
+
export declare function methods(): Promise<Method[]>;
|
|
96
|
+
/** An invoice, amounts in whole cents. */
|
|
97
|
+
export interface Invoice {
|
|
98
|
+
id: string;
|
|
99
|
+
numberStr?: string;
|
|
100
|
+
status?: string;
|
|
101
|
+
amountDue: number;
|
|
102
|
+
amountPaid?: number;
|
|
103
|
+
currency?: string;
|
|
104
|
+
createdAt?: string;
|
|
105
|
+
paidAt?: string;
|
|
106
|
+
}
|
|
107
|
+
export declare function invoices(): Promise<Invoice[]>;
|
|
108
|
+
/** Whole cents as money: `$1,234.56`. */
|
|
109
|
+
export declare function cents(n: number, currency?: string): string;
|
package/dist/lib/org.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The organization settings area's reads and writes, each on the API that owns it.
|
|
3
|
+
*
|
|
4
|
+
* the org row GET /v1/iam/organizations/admin/<org>
|
|
5
|
+
* delete the org DELETE /v1/iam/organizations/admin/<org>
|
|
6
|
+
* found a team org POST /v1/iam/organizations {owner: admin, name, displayName}
|
|
7
|
+
* business address GET|PUT /v1/settings/organization (cloud's per-org config)
|
|
8
|
+
* your keys GET|POST|DELETE /v1/account/keys (minted by IAM on you, shown once)
|
|
9
|
+
* the org's keys GET /v1/iam/keys?owner=<org>, DELETE /v1/iam/keys/<org>/<name>
|
|
10
|
+
* payment methods GET /v1/billing/methods
|
|
11
|
+
* invoices GET /v1/billing/invoices
|
|
12
|
+
*
|
|
13
|
+
* The name, the logo, the members and the invitations are lib/hanzo/team.ts's,
|
|
14
|
+
* and the plan and balance are lib/hanzo/tier.ts's. Nothing here decides who may
|
|
15
|
+
* do what: every request carries the reader's bearer and organization (`scope`),
|
|
16
|
+
* the server answers, and a refusal comes back as the server's own sentence.
|
|
17
|
+
*/
|
|
18
|
+
import { scope } from './session.js';
|
|
19
|
+
import { api } from './api.js';
|
|
20
|
+
import { list } from './list.js';
|
|
21
|
+
/** A refusal, with the status it came under and the server's words. */
|
|
22
|
+
export class Refusal extends Error {
|
|
23
|
+
status;
|
|
24
|
+
constructor(status, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.status = status;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** IAM's code for "not yours to do", said as a sentence. */
|
|
30
|
+
const UNAUTHORIZED = /^auth:Unauthorized operation$|^forbidden$/i;
|
|
31
|
+
const NOT_YOURS = 'Your role in this organization does not allow this.';
|
|
32
|
+
/** One request as the signed-in reader, in the organization this browser works in. */
|
|
33
|
+
async function ask(path, init = {}) {
|
|
34
|
+
const res = await fetch(`${api()}${path}`, {
|
|
35
|
+
method: init.method ?? 'GET',
|
|
36
|
+
headers: {
|
|
37
|
+
...scope(),
|
|
38
|
+
...(init.json === undefined ? null : { 'Content-Type': 'application/json' }),
|
|
39
|
+
},
|
|
40
|
+
body: init.json === undefined ? undefined : JSON.stringify(init.json),
|
|
41
|
+
});
|
|
42
|
+
let body = null;
|
|
43
|
+
try {
|
|
44
|
+
body = await res.json();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// An empty or HTML body carries nothing worth showing.
|
|
48
|
+
}
|
|
49
|
+
const said = (body ?? {});
|
|
50
|
+
if (res.status === 401)
|
|
51
|
+
throw new Refusal(401, 'Your session expired. Sign in again.');
|
|
52
|
+
if (!res.ok || said.status === 'error') {
|
|
53
|
+
const words = said.detail || said.msg || said.error || said.message || '';
|
|
54
|
+
const status = res.ok ? 400 : res.status;
|
|
55
|
+
throw new Refusal(status, UNAUTHORIZED.test(words) || (status === 403 && !words) ? NOT_YOURS : words || `The server answered ${res.status}.`);
|
|
56
|
+
}
|
|
57
|
+
return body;
|
|
58
|
+
}
|
|
59
|
+
const enc = encodeURIComponent;
|
|
60
|
+
/** The words of any failure, for a toast or a line under a control. */
|
|
61
|
+
export const say = (e) => (e instanceof Error && e.message ? e.message : 'That did not go through.');
|
|
62
|
+
export const readOrg = (org) => ask(`/v1/iam/organizations/admin/${enc(org)}`);
|
|
63
|
+
/** Deletes the org row. IAM decides who may; there is no undo. */
|
|
64
|
+
export const deleteOrg = (org) => ask(`/v1/iam/organizations/admin/${enc(org)}`, { method: 'DELETE' });
|
|
65
|
+
/**
|
|
66
|
+
* Creates an organization the reader owns: cloud's `POST /v1/account/orgs
|
|
67
|
+
* {name}`, the one create every surface uses. Cloud files the row in IAM and
|
|
68
|
+
* records the verified creator as its owner through IAM's membership API.
|
|
69
|
+
* Answers the new organization's handle; a refusal is what the reader is shown.
|
|
70
|
+
*/
|
|
71
|
+
export const createOrg = async (name) => {
|
|
72
|
+
const made = await ask('/v1/account/orgs', { method: 'POST', json: { name } });
|
|
73
|
+
if (!made?.org)
|
|
74
|
+
throw new Refusal(502, 'The organization was not created. Try again.');
|
|
75
|
+
return made.org;
|
|
76
|
+
};
|
|
77
|
+
/** The handle an org is addressed by, from the name a person typed. */
|
|
78
|
+
export function slug(name) {
|
|
79
|
+
return name
|
|
80
|
+
.normalize('NFKD')
|
|
81
|
+
.toLowerCase()
|
|
82
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
83
|
+
.replace(/^-+|-+$/g, '')
|
|
84
|
+
.slice(0, 39)
|
|
85
|
+
.replace(/-+$/, '');
|
|
86
|
+
}
|
|
87
|
+
export const EMPTY_ADDRESS = { line1: '', line2: '', city: '', state: '', postalCode: '', country: '' };
|
|
88
|
+
/** Where cloud keeps an org's own settings: one document per (org, product). */
|
|
89
|
+
const PROFILE = '/v1/settings/organization';
|
|
90
|
+
const text = (v) => (typeof v === 'string' ? v : '');
|
|
91
|
+
function addressOf(config) {
|
|
92
|
+
const a = (config?.address ?? {});
|
|
93
|
+
return {
|
|
94
|
+
line1: text(a.line1),
|
|
95
|
+
line2: text(a.line2),
|
|
96
|
+
city: text(a.city),
|
|
97
|
+
state: text(a.state),
|
|
98
|
+
postalCode: text(a.postalCode),
|
|
99
|
+
country: text(a.country),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export async function readAddress() {
|
|
103
|
+
return addressOf((await ask(PROFILE)).config);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Stores the address and answers what was stored. The document is written
|
|
107
|
+
* whole, so the rest of it is read first and carried: a save of the address
|
|
108
|
+
* never drops a field somebody else kept beside it.
|
|
109
|
+
*/
|
|
110
|
+
export async function saveAddress(next) {
|
|
111
|
+
const held = (await ask(PROFILE)).config ?? {};
|
|
112
|
+
const stored = await ask(PROFILE, { method: 'PUT', json: { config: { ...held, address: next } } });
|
|
113
|
+
return addressOf(stored.config);
|
|
114
|
+
}
|
|
115
|
+
export async function ownKeys() {
|
|
116
|
+
return list((await ask('/v1/account/keys')).keys);
|
|
117
|
+
}
|
|
118
|
+
/** Mints the reader's key of `type`, replacing the one they held, and answers it — once. */
|
|
119
|
+
export async function mintKey(type) {
|
|
120
|
+
const made = await ask('/v1/account/keys', { method: 'POST', json: { type } });
|
|
121
|
+
if (!made?.key)
|
|
122
|
+
throw new Refusal(502, 'IAM answered without a key.');
|
|
123
|
+
return made.key;
|
|
124
|
+
}
|
|
125
|
+
export const revokeOwnKey = (type) => ask(`/v1/account/keys?type=${type}`, { method: 'DELETE' });
|
|
126
|
+
export async function orgKeys(org) {
|
|
127
|
+
return list((await ask(`/v1/iam/keys?owner=${enc(org)}`)).keys);
|
|
128
|
+
}
|
|
129
|
+
export const revokeKey = (k) => ask(`/v1/iam/keys/${enc(k.owner)}/${enc(k.name)}`, { method: 'DELETE' });
|
|
130
|
+
export async function methods() {
|
|
131
|
+
const got = await ask('/v1/billing/methods');
|
|
132
|
+
return list(Array.isArray(got) ? got : got?.data);
|
|
133
|
+
}
|
|
134
|
+
export async function invoices() {
|
|
135
|
+
return list((await ask('/v1/billing/invoices')).invoices);
|
|
136
|
+
}
|
|
137
|
+
/** Whole cents as money: `$1,234.56`. */
|
|
138
|
+
export function cents(n, currency = 'usd') {
|
|
139
|
+
return new Intl.NumberFormat('en-US', { style: 'currency', currency: currency.toUpperCase() }).format(n / 100);
|
|
140
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a model's page lives. No imports, so a build script can ask too.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getOrgAndSlug(modelId: string): {
|
|
5
|
+
org: string;
|
|
6
|
+
slug: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function canonicalOrg(org?: string): string;
|
|
9
|
+
export declare function modelPagePath(modelId: string): string;
|
package/dist/lib/path.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a model's page lives. No imports, so a build script can ask too.
|
|
3
|
+
*/
|
|
4
|
+
export function getOrgAndSlug(modelId) {
|
|
5
|
+
if (modelId.includes('/')) {
|
|
6
|
+
const [org, ...rest] = modelId.split('/');
|
|
7
|
+
return { org, slug: rest.join('/') };
|
|
8
|
+
}
|
|
9
|
+
return { org: 'hanzo', slug: modelId };
|
|
10
|
+
}
|
|
11
|
+
// Some labs reach the gateway under two namespaces — `meta-llama` and `meta`,
|
|
12
|
+
// `bytedance-seed` and `bytedance` — and a leading `~` marks a latest-alias
|
|
13
|
+
// namespace of a lab already present. Fold them onto one canonical org so the
|
|
14
|
+
// catalogue heads one group per lab and that lab's page serves the union.
|
|
15
|
+
// Mirrors ProviderMark's OF map, which folds the same aliases for the mark.
|
|
16
|
+
const ORG_ALIASES = {
|
|
17
|
+
'meta-llama': 'meta',
|
|
18
|
+
'bytedance-seed': 'bytedance',
|
|
19
|
+
};
|
|
20
|
+
export function canonicalOrg(org) {
|
|
21
|
+
if (!org)
|
|
22
|
+
return 'hanzo';
|
|
23
|
+
const base = org.replace(/^~/, '');
|
|
24
|
+
return ORG_ALIASES[base] ?? base;
|
|
25
|
+
}
|
|
26
|
+
export function modelPagePath(modelId) {
|
|
27
|
+
const { org, slug } = getOrgAndSlug(modelId);
|
|
28
|
+
return `/models/${canonicalOrg(org)}/${slug}`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The subscription catalog, in one place, for every pricing surface.
|
|
3
|
+
*
|
|
4
|
+
* THERE ARE TWO CATALOGS AND THEY ARE DIFFERENT PRODUCTS. Confusing them is
|
|
5
|
+
* what this module exists to prevent:
|
|
6
|
+
*
|
|
7
|
+
* GET /v1/billing/plans THIS ONE. commerce's subscription authority — the
|
|
8
|
+
* same rows that charge. 17 plans across categories
|
|
9
|
+
* personal/team/enterprise/world/social/dns, each
|
|
10
|
+
* with `features`. Bare ARRAY, `slug`, `price` in
|
|
11
|
+
* CENTS.
|
|
12
|
+
* GET /v1/plans Cloud VM tiers — vcpus, memoryGB, diskGB. 11 rows,
|
|
13
|
+
* NO `features` on any of them. Shaped `{plans:[…]}`.
|
|
14
|
+
*
|
|
15
|
+
* They collide on the words "pro" and "enterprise" and on nothing else: `pro`
|
|
16
|
+
* the subscription is $20/mo, `pro` the VM tier is $25/mo for 2 vCPU. Reading
|
|
17
|
+
* the VM catalog to render subscriptions is not a wrong number, it is a wrong
|
|
18
|
+
* product — and it crashed this page. The team/enterprise view filtered /v1/plans
|
|
19
|
+
* for `category === "enterprise"`, matched the $429 VM row, replaced the real
|
|
20
|
+
* Team/Enterprise cards with it, and handed a card `features: undefined`. The
|
|
21
|
+
* `.map()` over it took the whole pricing page down to "Something went wrong."
|
|
22
|
+
* for anyone who clicked the tab.
|
|
23
|
+
*
|
|
24
|
+
* So the catalog is named once, mapped once, and guarded once, here.
|
|
25
|
+
*
|
|
26
|
+
* A SuperAdmin edits these rows at admin.hanzo.ai (/v1/commerce/plans/entries),
|
|
27
|
+
* and every reader here follows: the build bakes the authority's answer into the
|
|
28
|
+
* pricing snapshot for the first paint, and the page replaces it with the live
|
|
29
|
+
* read. No price in this repo is typed; @hanzo/plans is read only for what the
|
|
30
|
+
* wire does not carry — a plan's name for a slug, and the Free row's models.
|
|
31
|
+
*/
|
|
32
|
+
/** A row exactly as GET /v1/billing/plans returns it. */
|
|
33
|
+
export interface BillingPlan {
|
|
34
|
+
slug: string;
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
price: number | null;
|
|
38
|
+
priceAnnual?: number | null;
|
|
39
|
+
annualTotal?: number | null;
|
|
40
|
+
category: string;
|
|
41
|
+
features?: string[];
|
|
42
|
+
limits?: Record<string, number | null>;
|
|
43
|
+
popular?: boolean;
|
|
44
|
+
contactSales?: boolean;
|
|
45
|
+
perSeat?: boolean;
|
|
46
|
+
checkoutUrl?: string;
|
|
47
|
+
checkoutId?: string;
|
|
48
|
+
}
|
|
49
|
+
/** A plan as the pricing cards render it: dollars, and features always present. */
|
|
50
|
+
export interface SubscriptionPlan {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
description: string;
|
|
54
|
+
priceMonthly: number | null;
|
|
55
|
+
/** A year shown per month: what each month costs when a year is bought. */
|
|
56
|
+
priceAnnual?: number | null;
|
|
57
|
+
/** What a year charges, once, where the catalog sells a year: `annualTotal`. */
|
|
58
|
+
annualTotal?: number | null;
|
|
59
|
+
category: string;
|
|
60
|
+
popular?: boolean;
|
|
61
|
+
contactSales?: boolean;
|
|
62
|
+
pricePerUser?: boolean;
|
|
63
|
+
features: string[];
|
|
64
|
+
limits?: Record<string, number | null>;
|
|
65
|
+
payouts?: {
|
|
66
|
+
idleResalePercent: number;
|
|
67
|
+
description: string;
|
|
68
|
+
};
|
|
69
|
+
checkoutUrl?: string;
|
|
70
|
+
checkoutId?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The checkout address, opened on a plan when one is named, and returning to
|
|
74
|
+
* `back` once the buyer has paid. Without `back` a paid buyer lands on the pay
|
|
75
|
+
* site's receipt with no way into what they bought.
|
|
76
|
+
*
|
|
77
|
+
* ONE writer for this address. It was spelled out in three places — the
|
|
78
|
+
* personal ladder, the business strip and the /account hand-off — and a plan id
|
|
79
|
+
* only selects the card a reader clicked if every writer spells the query the
|
|
80
|
+
* same way.
|
|
81
|
+
*/
|
|
82
|
+
export declare function checkoutUrl(id?: string, back?: string): string;
|
|
83
|
+
/**
|
|
84
|
+
* A page on the pay site, for the organization this browser works in.
|
|
85
|
+
*
|
|
86
|
+
* pay charges and reads the ledger it is named, and it can only offer the ones
|
|
87
|
+
* the token lists; which of them this workspace is in is a fact only this site
|
|
88
|
+
* holds. `?org=` carries it across, and pay honours it only while the token
|
|
89
|
+
* offers it. Call it at the moment of leaving: the organization lives in this
|
|
90
|
+
* browser and moves with the switcher.
|
|
91
|
+
*/
|
|
92
|
+
export declare function payPage(path?: string): string;
|
|
93
|
+
/**
|
|
94
|
+
* The checkout a plan's CTA opens.
|
|
95
|
+
*
|
|
96
|
+
* A catalog row may carry its own link, and then that link wins; otherwise the
|
|
97
|
+
* id is the whole address. The id is also what `plan_clicked` reports, so the
|
|
98
|
+
* plan the event names and the plan the checkout opens cannot disagree.
|
|
99
|
+
*/
|
|
100
|
+
export declare function planCheckoutUrl(plan: SubscriptionPlan, back?: string): string;
|
|
101
|
+
/** A subscription term. A month is what every catalog row charges by default. */
|
|
102
|
+
export type Interval = "month" | "year";
|
|
103
|
+
/**
|
|
104
|
+
* A checkout address for a term: a year adds `interval=year`, a month leaves the
|
|
105
|
+
* address as it is. pay reads the parameter; until it sells a year, its cart
|
|
106
|
+
* states the monthly charge, and the buyer reads that before paying. A relative
|
|
107
|
+
* address is read against the pay site, where every checkout is.
|
|
108
|
+
*/
|
|
109
|
+
export declare function term(href: string, interval: Interval): string;
|
|
110
|
+
/**
|
|
111
|
+
* The cart for `seats` of a per-seat plan, for a term, charged to `org` and
|
|
112
|
+
* returning to `back` once paid. `quantity` is the seat count checkout sells.
|
|
113
|
+
*/
|
|
114
|
+
export declare function cartUrl(plan: string, seats: number, interval: Interval, org: string, back: string): string;
|
|
115
|
+
/** The plans in one category, live — or null when commerce did not answer. */
|
|
116
|
+
export declare function loadPlans(category: string): Promise<SubscriptionPlan[] | null>;
|
|
117
|
+
/** The plans in one category as the build last read them. */
|
|
118
|
+
export declare function fallbackPlans(category: string): SubscriptionPlan[];
|
|
119
|
+
/**
|
|
120
|
+
* Every row the authority serves, in catalog order: painted from the snapshot,
|
|
121
|
+
* replaced by the live read. This is how an edit made at admin.hanzo.ai reaches
|
|
122
|
+
* a reader with no build in between.
|
|
123
|
+
*/
|
|
124
|
+
export declare function useCatalog(): SubscriptionPlan[];
|
|
125
|
+
/** The row an organization runs on, priced. */
|
|
126
|
+
export interface OrgPlan {
|
|
127
|
+
plan: SubscriptionPlan | null;
|
|
128
|
+
/** The monthly price as money. An em dash until a row resolves — never a guess. */
|
|
129
|
+
price: string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Which plan an organization runs on, and what it costs. A plan is a
|
|
133
|
+
* subscription with usage limits; it mints no credit, so nothing here says it does.
|
|
134
|
+
*
|
|
135
|
+
* ONE ANSWER FOR TWO SURFACES. /pricing states the offer and the workspace gate
|
|
136
|
+
* asks a member to buy it; those were two readings of one row, each with its own
|
|
137
|
+
* copy of "the first personal row that charges" and its own money formatter. A
|
|
138
|
+
* marketing page and a subscribe screen quoting different numbers for the same
|
|
139
|
+
* plan is the failure that costs a sale, and it needs only for one of the two
|
|
140
|
+
* copies to be edited.
|
|
141
|
+
*
|
|
142
|
+
* It is the FIRST ROW THAT CHARGES in a category: `personal` is the plan a
|
|
143
|
+
* personal organization runs on, `team` the per-seat plan a team organization
|
|
144
|
+
* runs on, whatever commerce currently prices each at. Neither surface holds the
|
|
145
|
+
* figure, so neither can be wrong on its own.
|
|
146
|
+
*
|
|
147
|
+
* Painted from the snapshot and replaced by the live catalog, which is the
|
|
148
|
+
* contract every reader in this module keeps.
|
|
149
|
+
*/
|
|
150
|
+
export declare function usePlan(category?: string): OrgPlan;
|
|
151
|
+
/**
|
|
152
|
+
* A category's rows a reader can buy on their own, in catalog order: the
|
|
153
|
+
* personal ladder from Free up, or the Team plan. Rows sold through a
|
|
154
|
+
* conversation are left out. Painted from the snapshot, replaced by the catalog.
|
|
155
|
+
*/
|
|
156
|
+
export declare function usePlans(category: string): SubscriptionPlan[];
|
|
157
|
+
/** A row priced the way every surface writes it. */
|
|
158
|
+
export declare function priced(plan: SubscriptionPlan | null): OrgPlan;
|
|
159
|
+
/** Money as a sentence says it: whole dollars bare, anything else to the cent — $20, $16.67. */
|
|
160
|
+
export declare const money: (n: number) => string;
|
|
161
|
+
/**
|
|
162
|
+
* What one term of a plan charges, in dollars, or null where the catalog sells
|
|
163
|
+
* no such term. A month is `priceMonthly`. A year is the total the catalog
|
|
164
|
+
* states, or twelve of `priceAnnual` where it states only the monthly figure.
|
|
165
|
+
*/
|
|
166
|
+
export declare function charge(plan: SubscriptionPlan, interval: Interval): number | null;
|
|
167
|
+
/**
|
|
168
|
+
* A term's price as a button quotes it — "$20/month", "$200/year" — or null.
|
|
169
|
+
* A year is quoted as the total where the catalog states one, and otherwise as
|
|
170
|
+
* the monthly figure the catalog does state, never as a total derived here.
|
|
171
|
+
*/
|
|
172
|
+
export declare function quote(plan: SubscriptionPlan, interval: Interval): string | null;
|
|
173
|
+
/** What a year saves against twelve months, as a whole percent: 1 − year ÷ (12 × month). Null where it saves nothing. */
|
|
174
|
+
export declare function saving(plan: SubscriptionPlan): number | null;
|
|
175
|
+
/** Whether the Free plan runs a model: one its row names, or any `:free` route. */
|
|
176
|
+
export declare function onFree(model: string): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* The catalog's own name for a plan slug — "max-20x" reads as "Max 20x" —
|
|
179
|
+
* or null where the slug is empty or the catalog does not carry it.
|
|
180
|
+
*
|
|
181
|
+
* Read off the published package: a plan's name does not move between a build and the next release the way
|
|
182
|
+
* its price can, so the bundled copy is not a stale answer here the way it
|
|
183
|
+
* would be for money. A reader of `GET /v1/billing/tier`'s `plan` field
|
|
184
|
+
* needs a name and nothing else, so no live fetch is spent proving what the
|
|
185
|
+
* package already states.
|
|
186
|
+
*/
|
|
187
|
+
export declare function planName(slug: string | null | undefined): string | null;
|
|
188
|
+
/**
|
|
189
|
+
* A usage rate as money. A sub-dollar rate needs at least two decimals to read as
|
|
190
|
+
* a price at all — 0.1 is $0.10, not $0.1 — while keeping the precision the
|
|
191
|
+
* catalog states, so $0.015/1K vectors does not round away to a cent.
|
|
192
|
+
*/
|
|
193
|
+
export declare function formatUsageRate(v: number): string;
|