Skip to content

Commit 9420d80

Browse files
author
Frank
committed
zen: data share
1 parent c21161b commit 9420d80

27 files changed

Lines changed: 1444 additions & 755 deletions

File tree

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

github/sst-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
/// <reference path="../sst-env.d.ts" />
77

88
import "sst"
9-
export {}
9+
export {}

infra/console.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,7 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
9999
],
100100
})
101101

102-
const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY")
103-
const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY")
104-
const XAI_API_KEY = new sst.Secret("XAI_API_KEY")
105-
const BASETEN_API_KEY = new sst.Secret("BASETEN_API_KEY")
106-
const FIREWORKS_API_KEY = new sst.Secret("FIREWORKS_API_KEY")
102+
const ZEN_MODELS = new sst.Secret("ZEN_MODELS")
107103
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
108104
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
109105
properties: { value: auth.url.apply((url) => url!) },
@@ -128,17 +124,7 @@ if ($app.stage === "production" || $app.stage === "frank") {
128124
new sst.cloudflare.x.SolidStart("Console", {
129125
domain,
130126
path: "packages/console/app",
131-
link: [
132-
database,
133-
AUTH_API_URL,
134-
STRIPE_WEBHOOK_SECRET,
135-
STRIPE_SECRET_KEY,
136-
ANTHROPIC_API_KEY,
137-
OPENAI_API_KEY,
138-
XAI_API_KEY,
139-
BASETEN_API_KEY,
140-
FIREWORKS_API_KEY,
141-
],
127+
link: [database, AUTH_API_URL, STRIPE_WEBHOOK_SECRET, STRIPE_SECRET_KEY, ZEN_MODELS],
142128
environment: {
143129
//VITE_DOCS_URL: web.url.apply((url) => url!),
144130
//VITE_API_URL: gateway.url.apply((url) => url!),

packages/app/sst-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
/// <reference path="../../sst-env.d.ts" />
77

88
import "sst"
9-
export {}
9+
export {}

packages/console/app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@
1212
"dependencies": {
1313
"@ibm/plex": "6.4.1",
1414
"@openauthjs/openauth": "0.0.0-20250322224806",
15+
"@opencode/console-core": "workspace:*",
1516
"@solidjs/meta": "^0.29.4",
1617
"@solidjs/router": "^0.15.0",
1718
"@solidjs/start": "^1.1.0",
1819
"solid-js": "catalog:",
1920
"vinxi": "^0.5.7",
20-
"@opencode/console-core": "workspace:*"
21+
"zod": "catalog:"
2122
},
2223
"engines": {
2324
"node": ">=22"
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
.root {
2+
[data-slot="section-content"] {
3+
display: flex;
4+
flex-direction: column;
5+
gap: var(--space-3);
6+
}
7+
8+
[data-slot="reload-error"] {
9+
display: flex;
10+
align-items: center;
11+
justify-content: space-between;
12+
gap: var(--space-4);
13+
padding: var(--space-4);
14+
border: 1px solid var(--color-border);
15+
border-radius: var(--border-radius-sm);
16+
17+
p {
18+
color: var(--color-danger);
19+
font-size: var(--font-size-sm);
20+
line-height: 1.4;
21+
margin: 0;
22+
flex: 1;
23+
}
24+
25+
[data-slot="create-form"] {
26+
display: flex;
27+
gap: var(--space-2);
28+
margin: 0;
29+
flex-shrink: 0;
30+
}
31+
}
32+
[data-slot="payment"] {
33+
display: flex;
34+
flex-direction: column;
35+
gap: var(--space-3);
36+
padding: var(--space-4);
37+
border: 1px solid var(--color-border);
38+
border-radius: var(--border-radius-sm);
39+
min-width: 14.5rem;
40+
width: fit-content;
41+
42+
@media (max-width: 30rem) {
43+
width: 100%;
44+
}
45+
46+
[data-slot="credit-card"] {
47+
padding: var(--space-3-5) var(--space-4);
48+
background-color: var(--color-bg-surface);
49+
border-radius: var(--border-radius-sm);
50+
display: flex;
51+
align-items: center;
52+
justify-content: space-between;
53+
54+
[data-slot="card-icon"] {
55+
display: flex;
56+
align-items: center;
57+
color: var(--color-text-muted);
58+
}
59+
60+
[data-slot="card-details"] {
61+
display: flex;
62+
align-items: baseline;
63+
gap: var(--space-1);
64+
65+
[data-slot="secret"] {
66+
position: relative;
67+
bottom: 2px;
68+
font-size: var(--font-size-lg);
69+
color: var(--color-text-muted);
70+
font-weight: 400;
71+
}
72+
73+
[data-slot="number"] {
74+
font-size: var(--font-size-3xl);
75+
font-weight: 500;
76+
color: var(--color-text);
77+
}
78+
}
79+
}
80+
81+
[data-slot="button-row"] {
82+
display: flex;
83+
gap: var(--space-2);
84+
align-items: center;
85+
86+
@media (max-width: 30rem) {
87+
flex-direction: column;
88+
89+
> button {
90+
width: 100%;
91+
}
92+
}
93+
94+
[data-slot="create-form"] {
95+
margin: 0;
96+
}
97+
98+
/* Make Enable Billing button full width when it's the only button */
99+
> button {
100+
flex: 1;
101+
}
102+
}
103+
}
104+
[data-slot="usage"] {
105+
p {
106+
font-size: var(--font-size-sm);
107+
line-height: 1.5;
108+
color: var(--color-text-secondary);
109+
b {
110+
font-weight: 600;
111+
}
112+
}
113+
}
114+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router"
2+
import { withActor } from "~/context/auth.withActor"
3+
import styles from "./billing-section.module.css"
4+
import { Database, eq } from "@opencode/console-core/drizzle/index.js"
5+
import { WorkspaceTable } from "@opencode/console-core/schema/workspace.sql.js"
6+
import { Show } from "solid-js"
7+
8+
const updateShare = action(async (form: FormData) => {
9+
"use server"
10+
const workspaceID = form.get("workspaceID")?.toString()
11+
if (!workspaceID) return { error: "Workspace ID is required" }
12+
const dataShare = form.get("dataShare")?.toString() === "true" ? true : null
13+
return json(
14+
await withActor(() => {
15+
return Database.use((tx) =>
16+
tx
17+
.update(WorkspaceTable)
18+
.set({
19+
dataShare,
20+
})
21+
.where(eq(WorkspaceTable.id, workspaceID)),
22+
)
23+
}, workspaceID),
24+
{ revalidate: getWorkspaceInfo.key },
25+
)
26+
}, "workspace.disableShare")
27+
28+
const getWorkspaceInfo = query(async (workspaceID: string) => {
29+
"use server"
30+
return withActor(() => {
31+
return Database.use((tx) =>
32+
tx
33+
.select({
34+
dataShare: WorkspaceTable.dataShare,
35+
})
36+
.from(WorkspaceTable)
37+
.where(eq(WorkspaceTable.id, workspaceID))
38+
.then((r) => r[0]),
39+
)
40+
}, workspaceID)
41+
}, "workspace.get")
42+
43+
export function PrivacySection() {
44+
const params = useParams()
45+
const workspaceInfo = createAsync(() => getWorkspaceInfo(params.id))
46+
const updateShareSubmission = useSubmission(updateShare)
47+
48+
return (
49+
<section class={styles.root}>
50+
<div data-slot="section-title">
51+
<h2>Privacy controls</h2>
52+
<p>
53+
Some providers offer data-sharing programs. If you opt in, you voluntarily <b>share your usage data</b> with
54+
them, which they may use to improve their services, including <b>model training</b>.
55+
</p>
56+
<br />
57+
<p>
58+
By opting in, you gain access to <b>discounted pricing</b> from the provider. You can opt in or out at any
59+
time.
60+
</p>
61+
<br />
62+
<p>
63+
<a target="_blank" href="/docs/zen">
64+
Learn more
65+
</a>
66+
</p>
67+
</div>
68+
<Show when={workspaceInfo()?.dataShare}>
69+
<div data-slot="payment">
70+
<div data-slot="credit-card">
71+
<div data-slot="card-details">
72+
<span data-slot="number">You are currently opted in to the data-sharing program.</span>
73+
</div>
74+
</div>
75+
</div>
76+
</Show>
77+
<div data-slot="section-content">
78+
<div data-slot="payment">
79+
<div data-slot="button-row">
80+
<form action={updateShare} method="post" data-slot="create-form">
81+
<input type="hidden" name="workspaceID" value={params.id} />
82+
<input type="hidden" name="dataShare" value={workspaceInfo()?.dataShare ? "false" : "true"} />
83+
<button data-color="ghost" type="submit" disabled={updateShareSubmission.pending}>
84+
{workspaceInfo()?.dataShare
85+
? updateShareSubmission.pending
86+
? "Opting out..."
87+
: "Opt out"
88+
: updateShareSubmission.pending
89+
? "Opting in..."
90+
: "Opt in"}
91+
</button>
92+
</form>
93+
</div>
94+
</div>
95+
</div>
96+
</section>
97+
)
98+
}

packages/console/app/src/routes/workspace/[id].tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import "./[id].css"
22
import { MonthlyLimitSection } from "~/component/workspace/monthly-limit-section"
33
import { NewUserSection } from "~/component/workspace/new-user-section"
44
import { BillingSection } from "~/component/workspace/billing-section"
5+
import { PrivacySection } from "~/component/workspace/privacy-section"
56
import { PaymentSection } from "~/component/workspace/payment-section"
67
import { UsageSection } from "~/component/workspace/usage-section"
78
import { KeySection } from "~/component/workspace/key-section"
9+
import { Show } from "solid-js"
10+
import { useParams } from "@solidjs/router"
811

912
export default function () {
13+
const params = useParams()
1014
return (
1115
<div data-page="workspace-[id]">
1216
<section data-component="title-section">
@@ -25,9 +29,20 @@ export default function () {
2529
<KeySection />
2630
<BillingSection />
2731
<MonthlyLimitSection />
32+
<Show when={isBeta(params.id)}>
33+
<PrivacySection />
34+
</Show>
2835
<UsageSection />
2936
<PaymentSection />
3037
</div>
3138
</div>
3239
)
3340
}
41+
42+
export function isBeta(workspaceID: string) {
43+
return [
44+
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // production
45+
"wrk_01K4NFRR5P7FSYWH88307B4DDS", // dev
46+
"wrk_01K4PJRKJ2WPQZN3FFYRV4673F", // frank
47+
].includes(workspaceID)
48+
}

0 commit comments

Comments
 (0)