@heyo-sh/create-heyo-docs 0.4.0 → 1.0.1
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/deployments/cloudflare/astro/src/env.d.ts +5 -0
- package/deployments/cloudflare/astro/src/pages/heyo-docs-internal/ai-chat.ts +30 -0
- package/deployments/cloudflare/next/app/heyo-docs-internal/ai-chat/route.ts +25 -0
- package/deployments/cloudflare/next/cloudflare-env.d.ts +4 -0
- package/deployments/cloudflare/react-router/app/cloudflare-env.d.ts +4 -0
- package/deployments/cloudflare/react-router/app/lib/cloudflare-context.ts +4 -0
- package/deployments/cloudflare/react-router/app/routes/ai-chat.ts +30 -0
- package/deployments/cloudflare/react-router/workers/app.ts +6 -2
- package/dist/index.js +13 -12
- package/dist/project-name.d.ts +1 -1
- package/dist/project-name.d.ts.map +1 -1
- package/package.json +2 -2
- package/templates/astro/content/configuration.mdx +13 -6
- package/templates/astro/content/deploying.mdx +13 -0
- package/templates/astro/src/layouts/docs-layout.astro +256 -314
- package/templates/astro/src/pages/heyo-docs-internal/ai-chat.ts +11 -2
- package/templates/next/app/heyo-docs-internal/ai-chat/route.ts +8 -0
- package/templates/next/content/configuration.mdx +13 -6
- package/templates/next/content/deploying.mdx +13 -0
- package/templates/react-router/app/routes/ai-chat.ts +9 -1
- package/templates/react-router/content/configuration.mdx +13 -6
- package/templates/react-router/content/deploying.mdx +13 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createAiChatResponse } from "@heyo-sh/heyo-docs/ai";
|
|
2
|
+
import type { APIRoute } from "astro";
|
|
3
|
+
import { env } from "cloudflare:workers";
|
|
4
|
+
|
|
5
|
+
import config from "../../../heyo-docs.config";
|
|
6
|
+
import { pages as markdownPages } from "virtual:heyo-docs-content/server";
|
|
7
|
+
|
|
8
|
+
export const prerender = false;
|
|
9
|
+
|
|
10
|
+
export const POST: APIRoute = ({ request }) => {
|
|
11
|
+
const apiKey = env.OPENAI_API_KEY;
|
|
12
|
+
if (!apiKey)
|
|
13
|
+
return Response.json(
|
|
14
|
+
{ error: "The OPENAI_API_KEY Worker secret is not configured." },
|
|
15
|
+
{ status: 500 },
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return createAiChatResponse(request, {
|
|
19
|
+
ai: config.ai,
|
|
20
|
+
auth: { type: "api-key", token: apiKey },
|
|
21
|
+
markdownPages,
|
|
22
|
+
pages: markdownPages.map((page) => ({
|
|
23
|
+
description: page.description,
|
|
24
|
+
searchContent: page.raw,
|
|
25
|
+
slug: page.slug,
|
|
26
|
+
title: page.title,
|
|
27
|
+
})),
|
|
28
|
+
title: config.title,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createAiChatResponse } from "@heyo-sh/heyo-docs/ai";
|
|
2
|
+
import { getCloudflareContext } from "@opennextjs/cloudflare";
|
|
3
|
+
|
|
4
|
+
import config from "../../../heyo-docs.config";
|
|
5
|
+
import { docsPages, markdownPages } from "../../_heyo-docs/server";
|
|
6
|
+
|
|
7
|
+
export const runtime = "nodejs";
|
|
8
|
+
|
|
9
|
+
export async function POST(request: Request) {
|
|
10
|
+
const { env } = getCloudflareContext();
|
|
11
|
+
const apiKey = env.OPENAI_API_KEY;
|
|
12
|
+
if (!apiKey)
|
|
13
|
+
return Response.json(
|
|
14
|
+
{ error: "The OPENAI_API_KEY Worker secret is not configured." },
|
|
15
|
+
{ status: 500 },
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
return createAiChatResponse(request, {
|
|
19
|
+
ai: config.ai,
|
|
20
|
+
auth: { type: "api-key", token: apiKey },
|
|
21
|
+
markdownPages,
|
|
22
|
+
pages: docsPages,
|
|
23
|
+
title: config.title,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ActionFunctionArgs } from "react-router";
|
|
2
|
+
|
|
3
|
+
import config from "../../heyo-docs.config";
|
|
4
|
+
import { cloudflareContext } from "../lib/cloudflare-context";
|
|
5
|
+
import { pages } from "virtual:heyo-docs-content";
|
|
6
|
+
import { pages as markdownPages } from "virtual:heyo-docs-content/server";
|
|
7
|
+
|
|
8
|
+
export async function action({ context, request }: ActionFunctionArgs) {
|
|
9
|
+
if (request.method !== "POST")
|
|
10
|
+
return new Response("Method Not Allowed", {
|
|
11
|
+
headers: { Allow: "POST" },
|
|
12
|
+
status: 405,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const apiKey = context.get(cloudflareContext).OPENAI_API_KEY;
|
|
16
|
+
if (!apiKey)
|
|
17
|
+
return Response.json(
|
|
18
|
+
{ error: "The OPENAI_API_KEY Worker secret is not configured." },
|
|
19
|
+
{ status: 500 },
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const { createAiChatResponse } = await import("@heyo-sh/heyo-docs/ai");
|
|
23
|
+
return createAiChatResponse(request, {
|
|
24
|
+
ai: config.ai,
|
|
25
|
+
auth: { type: "api-key", token: apiKey },
|
|
26
|
+
markdownPages,
|
|
27
|
+
pages,
|
|
28
|
+
title: config.title,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { createRequestHandler, RouterContextProvider } from "react-router";
|
|
2
2
|
|
|
3
|
+
import { cloudflareContext } from "../app/lib/cloudflare-context";
|
|
4
|
+
|
|
3
5
|
const handleRequest = createRequestHandler(
|
|
4
6
|
() => import("virtual:react-router/server-build"),
|
|
5
7
|
import.meta.env.MODE,
|
|
6
8
|
);
|
|
7
9
|
|
|
8
10
|
export default {
|
|
9
|
-
fetch(request) {
|
|
10
|
-
|
|
11
|
+
fetch(request, env) {
|
|
12
|
+
const context = new RouterContextProvider();
|
|
13
|
+
context.set(cloudflareContext, env);
|
|
14
|
+
return handleRequest(request, context);
|
|
11
15
|
},
|
|
12
16
|
} satisfies ExportedHandler<Env>;
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ function packageManagerRunCommand(manager, script) {
|
|
|
47
47
|
// src/project-name.ts
|
|
48
48
|
var PROJECT_NAME_PATTERN = /^[a-z0-9][a-z0-9-]*$/i;
|
|
49
49
|
function projectNameError(value) {
|
|
50
|
-
if (!value
|
|
50
|
+
if (!value?.trim())
|
|
51
51
|
return "Enter a project name.";
|
|
52
52
|
if (!PROJECT_NAME_PATTERN.test(value))
|
|
53
53
|
return "Use letters, numbers and hyphens only.";
|
|
@@ -289,14 +289,12 @@ function parseArguments(argv) {
|
|
|
289
289
|
}
|
|
290
290
|
async function askQuestions(defaults) {
|
|
291
291
|
p.intro("Create Heyo Docs");
|
|
292
|
-
const projectName = await p.text({
|
|
292
|
+
const projectName = promptValue(await p.text({
|
|
293
293
|
message: "Project name",
|
|
294
294
|
placeholder: "my-docs",
|
|
295
295
|
defaultValue: "my-docs",
|
|
296
296
|
validate: projectNameError
|
|
297
|
-
});
|
|
298
|
-
if (p.isCancel(projectName))
|
|
299
|
-
cancelCreation();
|
|
297
|
+
}));
|
|
300
298
|
const template = await selectAvailable("Template", TEMPLATE_OPTIONS, [
|
|
301
299
|
"react-router",
|
|
302
300
|
"next",
|
|
@@ -309,12 +307,10 @@ async function askQuestions(defaults) {
|
|
|
309
307
|
"moss"
|
|
310
308
|
]);
|
|
311
309
|
const packageManager = await selectAvailable("Package manager", PACKAGE_MANAGER_OPTIONS, ["bun", "pnpm", "npm", "yarn"], defaults.packageManager);
|
|
312
|
-
const install = await p.confirm({
|
|
310
|
+
const install = promptValue(await p.confirm({
|
|
313
311
|
message: "Install dependencies?",
|
|
314
312
|
initialValue: true
|
|
315
|
-
});
|
|
316
|
-
if (p.isCancel(install))
|
|
317
|
-
cancelCreation();
|
|
313
|
+
}));
|
|
318
314
|
return {
|
|
319
315
|
projectName,
|
|
320
316
|
template,
|
|
@@ -334,14 +330,19 @@ async function selectDeployment(template, initialValue) {
|
|
|
334
330
|
}
|
|
335
331
|
async function selectAvailable(message, options, available, initialValue) {
|
|
336
332
|
for (;; ) {
|
|
337
|
-
const answer = await p.select({ message, options, initialValue });
|
|
338
|
-
if (p.isCancel(answer))
|
|
339
|
-
cancelCreation();
|
|
333
|
+
const answer = promptValue(await p.select({ message, options, initialValue }));
|
|
340
334
|
if (isOneOf(answer, available))
|
|
341
335
|
return answer;
|
|
342
336
|
p.log.warn("That option is planned but not available in this MVP.");
|
|
343
337
|
}
|
|
344
338
|
}
|
|
339
|
+
function promptValue(value) {
|
|
340
|
+
if (typeof value !== "symbol")
|
|
341
|
+
return value;
|
|
342
|
+
if (p.isCancel(value))
|
|
343
|
+
cancelCreation();
|
|
344
|
+
throw new Error("Prompt did not return a value.");
|
|
345
|
+
}
|
|
345
346
|
function isOneOf(value, values) {
|
|
346
347
|
return values.some((candidate) => candidate === value);
|
|
347
348
|
}
|
package/dist/project-name.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare function projectNameError(value: string): string | undefined;
|
|
1
|
+
export declare function projectNameError(value: string | undefined): string | undefined;
|
|
2
2
|
export declare function assertProjectName(value: string): string;
|
|
3
3
|
//# sourceMappingURL=project-name.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-name.d.ts","sourceRoot":"","sources":["../src/project-name.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"project-name.d.ts","sourceRoot":"","sources":["../src/project-name.ts"],"names":[],"mappings":"AAEA,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyo-sh/create-heyo-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Create a standalone Heyo Docs documentation project.",
|
|
5
5
|
"homepage": "https://heyo.sh",
|
|
6
6
|
"repository": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@clack/prompts": "
|
|
30
|
+
"@clack/prompts": "1.8.0"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"dev": "bun run build --watch",
|
|
@@ -66,15 +66,15 @@ sidebar entry should point to a page outside the local documentation.
|
|
|
66
66
|
|
|
67
67
|
## AI chat
|
|
68
68
|
|
|
69
|
-
Add `ai.chat` to enable a documentation-aware chat
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
Add `ai.chat` to enable a documentation-aware chat through [Pi](https://github.com/badlogic/pi-mono).
|
|
70
|
+
Use any provider identifier bundled with the installed Pi version, such as
|
|
71
|
+
`openai`, `anthropic`, `google`, `openrouter`, or `xai`. Authentication stays
|
|
72
|
+
server-only and is supplied by the route that handles the chat request.
|
|
72
73
|
|
|
73
74
|
<CodeBlock language="ts" title="heyo-docs.config.ts">
|
|
74
75
|
{`ai: {
|
|
75
76
|
chat: {
|
|
76
77
|
provider: "openai",
|
|
77
|
-
key: process.env.OPENAI_API_KEY!,
|
|
78
78
|
model: "gpt-5-mini",
|
|
79
79
|
variant: "right", // "center" shows the compact prompt first
|
|
80
80
|
icon: "chat",
|
|
@@ -85,8 +85,15 @@ server environment variable; Heyo Docs sends it only from the chat endpoint.
|
|
|
85
85
|
},`}
|
|
86
86
|
</CodeBlock>
|
|
87
87
|
|
|
88
|
-
`provider
|
|
89
|
-
|
|
88
|
+
`provider` and `model` are required. Keep the credential out of this
|
|
89
|
+
configuration and pass it as `auth` to `createAiChatResponse` in the server
|
|
90
|
+
route. Cloudflare routes obtain the key from the Worker `env` binding; Node.js
|
|
91
|
+
routes can read their server environment there. GitHub Copilot and OpenAI Codex
|
|
92
|
+
use `auth: { type: "oauth", getAccessToken }`; Amazon Bedrock uses either
|
|
93
|
+
`{ type: "aws", region?, profile? }` and the AWS credential chain, or
|
|
94
|
+
`{ type: "bedrock-bearer", token, region? }`. The model must be an actual model
|
|
95
|
+
identifier, not the provider name. Amazon Bedrock requires a Node.js chat
|
|
96
|
+
endpoint. `variant` defaults to `right`. `name`
|
|
90
97
|
defaults to `AI`; it is used for the drawer title and the assistant's message
|
|
91
98
|
label. `placeholder` defaults to
|
|
92
99
|
`Ask AI about the docs` and is shared by the compact prompt and drawer input.
|
|
@@ -30,6 +30,19 @@ the sitemap, RSS feed, robots file, and AI-facing documentation endpoints.
|
|
|
30
30
|
Use the equivalent `npm run build`, `pnpm build`, or `yarn build` command
|
|
31
31
|
when the project uses a different package manager.
|
|
32
32
|
|
|
33
|
+
## Cloudflare secrets
|
|
34
|
+
|
|
35
|
+
For AI chat, save the provider key as a Worker secret:
|
|
36
|
+
|
|
37
|
+
<CodeBlock language="bash" title="Terminal">
|
|
38
|
+
{"bunx wrangler secret put OPENAI_API_KEY"}
|
|
39
|
+
</CodeBlock>
|
|
40
|
+
|
|
41
|
+
Keep the credential out of `heyo-docs.config.ts`. The Cloudflare overlay reads
|
|
42
|
+
the native Worker `env.OPENAI_API_KEY` binding in the AI route, after the
|
|
43
|
+
request reaches the Worker. It is never part of the documentation build or the
|
|
44
|
+
browser bundle.
|
|
45
|
+
|
|
33
46
|
<Callout type="tip" title="Set the canonical URL">
|
|
34
47
|
Add the final public origin as <code>siteUrl</code> in
|
|
35
48
|
<code>heyo-docs.config.ts</code> before the first production release. It keeps
|
|
@@ -188,20 +188,16 @@ const zammadBootstrap = zammad ? zammadBootstrapScript() : undefined;
|
|
|
188
188
|
<!doctype html>
|
|
189
189
|
<html lang="en">
|
|
190
190
|
<head>
|
|
191
|
-
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
src={transcend.src}
|
|
202
|
-
/>
|
|
203
|
-
)
|
|
204
|
-
}
|
|
191
|
+
{transcendGoogleConsentDefaults && (
|
|
192
|
+
<script is:inline set:html={transcendGoogleConsentDefaults}></script>
|
|
193
|
+
)}
|
|
194
|
+
{transcend && (
|
|
195
|
+
<script
|
|
196
|
+
is:inline
|
|
197
|
+
data-cfasync={transcend.dataCfasync}
|
|
198
|
+
src={transcend.src}
|
|
199
|
+
></script>
|
|
200
|
+
)}
|
|
205
201
|
<meta charset="utf-8" />
|
|
206
202
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
207
203
|
<meta name="color-scheme" content="light dark" />
|
|
@@ -223,334 +219,280 @@ const zammadBootstrap = zammad ? zammadBootstrapScript() : undefined;
|
|
|
223
219
|
<link rel="icon" href="/favicon-96x96.png" sizes="96x96" type="image/png" />
|
|
224
220
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" sizes="180x180" />
|
|
225
221
|
<link rel="manifest" href="/site.webmanifest" />
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
set:html={googleTagManager}
|
|
245
|
-
/>
|
|
246
|
-
)
|
|
247
|
-
}
|
|
222
|
+
{hasChangelog && (
|
|
223
|
+
<link
|
|
224
|
+
rel="alternate"
|
|
225
|
+
href="/rss.xml"
|
|
226
|
+
type="application/rss+xml"
|
|
227
|
+
title={`${config.title} updates`}
|
|
228
|
+
/>
|
|
229
|
+
)}
|
|
230
|
+
{osano && <script is:inline src={osano.src}></script>}
|
|
231
|
+
{googleTagManager && (
|
|
232
|
+
<script
|
|
233
|
+
is:inline
|
|
234
|
+
data-google-tag-manager-container-id={
|
|
235
|
+
config.integrations.analytics.gtm?.containerId
|
|
236
|
+
}
|
|
237
|
+
set:html={googleTagManager}
|
|
238
|
+
></script>
|
|
239
|
+
)}
|
|
248
240
|
<script
|
|
249
241
|
is:inline
|
|
250
242
|
set:html={getThemeScript(THEME_STORAGE_KEY, config.mode)}
|
|
251
|
-
|
|
252
|
-
{adobe && <script is:inline async src={adobe.src}
|
|
253
|
-
{amplitude && <script is:inline src={amplitude.src}
|
|
254
|
-
{
|
|
255
|
-
|
|
243
|
+
></script>
|
|
244
|
+
{adobe && <script is:inline async src={adobe.src}></script>}
|
|
245
|
+
{amplitude && <script is:inline src={amplitude.src}></script>}
|
|
246
|
+
{clarity && (
|
|
247
|
+
<script
|
|
248
|
+
is:inline
|
|
249
|
+
data-clarity-project-id={
|
|
250
|
+
config.integrations.analytics.clarity?.projectId
|
|
251
|
+
}
|
|
252
|
+
set:html={clarity}
|
|
253
|
+
></script>
|
|
254
|
+
)}
|
|
255
|
+
{clearbit && <script is:inline src={clearbit.src}></script>}
|
|
256
|
+
{fathom && (
|
|
257
|
+
<script
|
|
258
|
+
is:inline
|
|
259
|
+
data-site={fathom.siteId}
|
|
260
|
+
data-spa={fathom.spa}
|
|
261
|
+
defer={fathom.defer}
|
|
262
|
+
src={fathom.src}
|
|
263
|
+
></script>
|
|
264
|
+
)}
|
|
265
|
+
{googleAnalytics && googleAnalyticsBootstrap && (
|
|
266
|
+
<>
|
|
256
267
|
<script
|
|
257
268
|
is:inline
|
|
258
|
-
|
|
259
|
-
|
|
269
|
+
async={googleAnalytics.async}
|
|
270
|
+
src={googleAnalytics.src}
|
|
271
|
+
></script>
|
|
272
|
+
<script
|
|
273
|
+
is:inline
|
|
274
|
+
data-google-analytics-measurement-id={
|
|
275
|
+
config.integrations.analytics.ga4?.measurementId
|
|
260
276
|
}
|
|
261
|
-
set:html={
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
{
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
set:html={googleAnalyticsBootstrap}
|
|
278
|
+
></script>
|
|
279
|
+
</>
|
|
280
|
+
)}
|
|
281
|
+
{heap && (
|
|
282
|
+
<script
|
|
283
|
+
is:inline
|
|
284
|
+
data-heap-environment-id={
|
|
285
|
+
config.integrations.analytics.heap?.environmentId
|
|
286
|
+
}
|
|
287
|
+
set:html={heap}
|
|
288
|
+
></script>
|
|
289
|
+
)}
|
|
290
|
+
{hotjar && (
|
|
291
|
+
<script
|
|
292
|
+
is:inline
|
|
293
|
+
data-hotjar-site-id={config.integrations.analytics.hotjar?.siteId}
|
|
294
|
+
data-hotjar-snippet-version={
|
|
295
|
+
config.integrations.analytics.hotjar?.snippetVersion
|
|
296
|
+
}
|
|
297
|
+
set:html={hotjar}
|
|
298
|
+
></script>
|
|
299
|
+
)}
|
|
300
|
+
{logRocket && logRocketBootstrap && (
|
|
301
|
+
<>
|
|
268
302
|
<script
|
|
269
303
|
is:inline
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
src={fathom.src}
|
|
274
|
-
/>
|
|
275
|
-
)
|
|
276
|
-
}
|
|
277
|
-
{
|
|
278
|
-
googleAnalytics && googleAnalyticsBootstrap && (
|
|
279
|
-
<>
|
|
280
|
-
<script
|
|
281
|
-
is:inline
|
|
282
|
-
async={googleAnalytics.async}
|
|
283
|
-
src={googleAnalytics.src}
|
|
284
|
-
/>
|
|
285
|
-
<script
|
|
286
|
-
is:inline
|
|
287
|
-
data-google-analytics-measurement-id={
|
|
288
|
-
config.integrations.analytics.ga4?.measurementId
|
|
289
|
-
}
|
|
290
|
-
set:html={googleAnalyticsBootstrap}
|
|
291
|
-
/>
|
|
292
|
-
</>
|
|
293
|
-
)
|
|
294
|
-
}
|
|
295
|
-
{
|
|
296
|
-
heap && (
|
|
304
|
+
crossorigin={logRocket.crossOrigin}
|
|
305
|
+
src={logRocket.src}
|
|
306
|
+
></script>
|
|
297
307
|
<script
|
|
298
308
|
is:inline
|
|
299
|
-
data-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
hotjar && (
|
|
309
|
+
data-logrocket-app-id={config.integrations.analytics.logrocket?.appId}
|
|
310
|
+
set:html={logRocketBootstrap}
|
|
311
|
+
></script>
|
|
312
|
+
</>
|
|
313
|
+
)}
|
|
314
|
+
{mixpanel && mixpanelBootstrap && (
|
|
315
|
+
<>
|
|
316
|
+
<script is:inline src={mixpanel.src}></script>
|
|
308
317
|
<script
|
|
309
318
|
is:inline
|
|
310
|
-
data-
|
|
311
|
-
|
|
312
|
-
config.integrations.analytics.hotjar?.snippetVersion
|
|
319
|
+
data-mixpanel-project-token={
|
|
320
|
+
config.integrations.analytics.mixpanel?.projectToken
|
|
313
321
|
}
|
|
314
|
-
set:html={
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
<>
|
|
321
|
-
<script
|
|
322
|
-
is:inline
|
|
323
|
-
crossorigin={logRocket.crossOrigin}
|
|
324
|
-
src={logRocket.src}
|
|
325
|
-
/>
|
|
326
|
-
<script
|
|
327
|
-
is:inline
|
|
328
|
-
data-logrocket-app-id={
|
|
329
|
-
config.integrations.analytics.logrocket?.appId
|
|
330
|
-
}
|
|
331
|
-
set:html={logRocketBootstrap}
|
|
332
|
-
/>
|
|
333
|
-
</>
|
|
334
|
-
)
|
|
335
|
-
}
|
|
336
|
-
{
|
|
337
|
-
mixpanel && mixpanelBootstrap && (
|
|
338
|
-
<>
|
|
339
|
-
<script is:inline src={mixpanel.src} />
|
|
340
|
-
<script
|
|
341
|
-
is:inline
|
|
342
|
-
data-mixpanel-project-token={
|
|
343
|
-
config.integrations.analytics.mixpanel?.projectToken
|
|
344
|
-
}
|
|
345
|
-
set:html={mixpanelBootstrap}
|
|
346
|
-
/>
|
|
347
|
-
</>
|
|
348
|
-
)
|
|
349
|
-
}
|
|
350
|
-
{
|
|
351
|
-
openpanel && openpanelBootstrap && (
|
|
352
|
-
<>
|
|
353
|
-
<script
|
|
354
|
-
is:inline
|
|
355
|
-
data-openpanel-api-url={
|
|
356
|
-
config.integrations.analytics.openpanel?.apiUrl
|
|
357
|
-
}
|
|
358
|
-
data-openpanel-client-id={
|
|
359
|
-
config.integrations.analytics.openpanel?.clientId
|
|
360
|
-
}
|
|
361
|
-
set:html={openpanelBootstrap}
|
|
362
|
-
/>
|
|
363
|
-
<script
|
|
364
|
-
is:inline
|
|
365
|
-
async={openpanel.async}
|
|
366
|
-
defer={openpanel.defer}
|
|
367
|
-
src={openpanel.src}
|
|
368
|
-
/>
|
|
369
|
-
</>
|
|
370
|
-
)
|
|
371
|
-
}
|
|
372
|
-
{
|
|
373
|
-
openReplay && (
|
|
322
|
+
set:html={mixpanelBootstrap}
|
|
323
|
+
></script>
|
|
324
|
+
</>
|
|
325
|
+
)}
|
|
326
|
+
{openpanel && openpanelBootstrap && (
|
|
327
|
+
<>
|
|
374
328
|
<script
|
|
375
329
|
is:inline
|
|
376
|
-
data-
|
|
377
|
-
config.integrations.analytics.
|
|
330
|
+
data-openpanel-api-url={
|
|
331
|
+
config.integrations.analytics.openpanel?.apiUrl
|
|
378
332
|
}
|
|
379
|
-
data-
|
|
380
|
-
config.integrations.analytics.
|
|
333
|
+
data-openpanel-client-id={
|
|
334
|
+
config.integrations.analytics.openpanel?.clientId
|
|
381
335
|
}
|
|
382
|
-
set:html={
|
|
383
|
-
|
|
384
|
-
)
|
|
385
|
-
}
|
|
386
|
-
{
|
|
387
|
-
pirsch && (
|
|
388
|
-
<script
|
|
389
|
-
is:inline
|
|
390
|
-
data-code={pirsch.code}
|
|
391
|
-
defer={pirsch.defer}
|
|
392
|
-
src={pirsch.src}
|
|
393
|
-
/>
|
|
394
|
-
)
|
|
395
|
-
}
|
|
396
|
-
{
|
|
397
|
-
plausible && (
|
|
398
|
-
<script
|
|
399
|
-
is:inline
|
|
400
|
-
data-domain={plausible.domain}
|
|
401
|
-
defer={plausible.defer}
|
|
402
|
-
src={plausible.src}
|
|
403
|
-
/>
|
|
404
|
-
)
|
|
405
|
-
}
|
|
406
|
-
{
|
|
407
|
-
posthog && posthogBootstrap && posthogHost && (
|
|
408
|
-
<>
|
|
409
|
-
<script is:inline src={posthog.src} />
|
|
410
|
-
<script
|
|
411
|
-
is:inline
|
|
412
|
-
data-posthog-api-host={posthogHost}
|
|
413
|
-
data-posthog-project-api-key={
|
|
414
|
-
config.integrations.analytics.posthog?.projectApiKey
|
|
415
|
-
}
|
|
416
|
-
set:html={posthogBootstrap}
|
|
417
|
-
/>
|
|
418
|
-
</>
|
|
419
|
-
)
|
|
420
|
-
}
|
|
421
|
-
{
|
|
422
|
-
rybbit && (
|
|
423
|
-
<script
|
|
424
|
-
is:inline
|
|
425
|
-
data-site-id={rybbit.siteId}
|
|
426
|
-
defer={rybbit.defer}
|
|
427
|
-
src={rybbit.src}
|
|
428
|
-
/>
|
|
429
|
-
)
|
|
430
|
-
}
|
|
431
|
-
{
|
|
432
|
-
umami && (
|
|
336
|
+
set:html={openpanelBootstrap}
|
|
337
|
+
></script>
|
|
433
338
|
<script
|
|
434
339
|
is:inline
|
|
435
|
-
|
|
436
|
-
defer={
|
|
437
|
-
src={
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
}
|
|
441
|
-
{
|
|
442
|
-
|
|
340
|
+
async={openpanel.async}
|
|
341
|
+
defer={openpanel.defer}
|
|
342
|
+
src={openpanel.src}
|
|
343
|
+
></script>
|
|
344
|
+
</>
|
|
345
|
+
)}
|
|
346
|
+
{openReplay && (
|
|
347
|
+
<script
|
|
348
|
+
is:inline
|
|
349
|
+
data-openreplay-ingest-point={
|
|
350
|
+
config.integrations.analytics.openreplay?.ingestPoint
|
|
351
|
+
}
|
|
352
|
+
data-openreplay-project-key={
|
|
353
|
+
config.integrations.analytics.openreplay?.projectKey
|
|
354
|
+
}
|
|
355
|
+
set:html={openReplay}
|
|
356
|
+
></script>
|
|
357
|
+
)}
|
|
358
|
+
{pirsch && (
|
|
359
|
+
<script
|
|
360
|
+
is:inline
|
|
361
|
+
data-code={pirsch.code}
|
|
362
|
+
defer={pirsch.defer}
|
|
363
|
+
src={pirsch.src}
|
|
364
|
+
></script>
|
|
365
|
+
)}
|
|
366
|
+
{plausible && (
|
|
367
|
+
<script
|
|
368
|
+
is:inline
|
|
369
|
+
data-domain={plausible.domain}
|
|
370
|
+
defer={plausible.defer}
|
|
371
|
+
src={plausible.src}
|
|
372
|
+
></script>
|
|
373
|
+
)}
|
|
374
|
+
{posthog && posthogBootstrap && posthogHost && (
|
|
375
|
+
<>
|
|
376
|
+
<script is:inline src={posthog.src}></script>
|
|
443
377
|
<script
|
|
444
378
|
is:inline
|
|
445
|
-
data-
|
|
446
|
-
data-
|
|
447
|
-
|
|
448
|
-
set:html={chaskiq}
|
|
449
|
-
/>
|
|
450
|
-
)
|
|
451
|
-
}
|
|
452
|
-
{
|
|
453
|
-
papercups && (
|
|
454
|
-
<script
|
|
455
|
-
is:inline
|
|
456
|
-
data-papercups-base-url={
|
|
457
|
-
config.integrations.support.papercups?.baseUrl
|
|
379
|
+
data-posthog-api-host={posthogHost}
|
|
380
|
+
data-posthog-project-api-key={
|
|
381
|
+
config.integrations.analytics.posthog?.projectApiKey
|
|
458
382
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
383
|
+
set:html={posthogBootstrap}
|
|
384
|
+
></script>
|
|
385
|
+
</>
|
|
386
|
+
)}
|
|
387
|
+
{rybbit && (
|
|
388
|
+
<script
|
|
389
|
+
is:inline
|
|
390
|
+
data-site-id={rybbit.siteId}
|
|
391
|
+
defer={rybbit.defer}
|
|
392
|
+
src={rybbit.src}
|
|
393
|
+
></script>
|
|
394
|
+
)}
|
|
395
|
+
{umami && (
|
|
396
|
+
<script
|
|
397
|
+
is:inline
|
|
398
|
+
data-website-id={umami.websiteId}
|
|
399
|
+
defer={umami.defer}
|
|
400
|
+
src={umami.src}
|
|
401
|
+
></script>
|
|
402
|
+
)}
|
|
403
|
+
{chaskiq && (
|
|
404
|
+
<script
|
|
405
|
+
is:inline
|
|
406
|
+
data-chaskiq-app-id={config.integrations.support.chaskiq?.appId}
|
|
407
|
+
data-chaskiq-base-url={config.integrations.support.chaskiq?.baseUrl}
|
|
408
|
+
data-chaskiq-ws-url={config.integrations.support.chaskiq?.wsUrl}
|
|
409
|
+
set:html={chaskiq}
|
|
410
|
+
></script>
|
|
411
|
+
)}
|
|
412
|
+
{papercups && (
|
|
413
|
+
<script
|
|
414
|
+
is:inline
|
|
415
|
+
data-papercups-base-url={config.integrations.support.papercups?.baseUrl}
|
|
416
|
+
data-papercups-inbox={config.integrations.support.papercups?.inbox}
|
|
417
|
+
data-papercups-token={config.integrations.support.papercups?.token}
|
|
418
|
+
set:html={papercups}
|
|
419
|
+
></script>
|
|
420
|
+
)}
|
|
421
|
+
{typebot && (
|
|
422
|
+
<script
|
|
423
|
+
is:inline
|
|
424
|
+
data-heyo-typebot
|
|
425
|
+
data-typebot-api-host={config.integrations.support.typebot?.apiHost}
|
|
426
|
+
data-typebot-id={config.integrations.support.typebot?.typebot}
|
|
427
|
+
set:html={typebot}
|
|
428
|
+
type="module"
|
|
429
|
+
></script>
|
|
430
|
+
)}
|
|
431
|
+
<script is:inline type="application/ld+json" set:html={jsonLd}></script>
|
|
478
432
|
</head>
|
|
479
433
|
<body>
|
|
480
|
-
{
|
|
481
|
-
|
|
482
|
-
<
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
)
|
|
492
|
-
}
|
|
434
|
+
{googleTagManagerNoScript && (
|
|
435
|
+
<noscript>
|
|
436
|
+
<iframe
|
|
437
|
+
height="0"
|
|
438
|
+
src={googleTagManagerNoScript}
|
|
439
|
+
style="display:none;visibility:hidden"
|
|
440
|
+
title="Google Tag Manager"
|
|
441
|
+
width="0"
|
|
442
|
+
></iframe>
|
|
443
|
+
</noscript>
|
|
444
|
+
)}
|
|
493
445
|
<slot />
|
|
494
|
-
{
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
<script is:inline defer={swetrix.defer} src={swetrix.src} />
|
|
498
|
-
<script
|
|
499
|
-
is:inline
|
|
500
|
-
data-swetrix-api-url={config.integrations.analytics.swetrix?.apiUrl}
|
|
501
|
-
data-swetrix-project-id={
|
|
502
|
-
config.integrations.analytics.swetrix?.projectId
|
|
503
|
-
}
|
|
504
|
-
set:html={swetrixBootstrap}
|
|
505
|
-
/>
|
|
506
|
-
</>
|
|
507
|
-
)
|
|
508
|
-
}
|
|
509
|
-
{
|
|
510
|
-
intercom && (
|
|
446
|
+
{swetrix && swetrixBootstrap && (
|
|
447
|
+
<>
|
|
448
|
+
<script is:inline defer={swetrix.defer} src={swetrix.src}></script>
|
|
511
449
|
<script
|
|
512
450
|
is:inline
|
|
513
|
-
data-
|
|
514
|
-
data-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
chatwoot && (
|
|
451
|
+
data-swetrix-api-url={config.integrations.analytics.swetrix?.apiUrl}
|
|
452
|
+
data-swetrix-project-id={
|
|
453
|
+
config.integrations.analytics.swetrix?.projectId
|
|
454
|
+
}
|
|
455
|
+
set:html={swetrixBootstrap}
|
|
456
|
+
></script>
|
|
457
|
+
</>
|
|
458
|
+
)}
|
|
459
|
+
{intercom && (
|
|
460
|
+
<script
|
|
461
|
+
is:inline
|
|
462
|
+
data-intercom-app-id={config.integrations.support.intercom?.appId}
|
|
463
|
+
data-intercom-api-base={config.integrations.support.intercom?.apiBase}
|
|
464
|
+
set:html={intercom}
|
|
465
|
+
></script>
|
|
466
|
+
)}
|
|
467
|
+
{front && frontBootstrap && (
|
|
468
|
+
<>
|
|
469
|
+
<script is:inline src={front.src}></script>
|
|
533
470
|
<script
|
|
534
471
|
is:inline
|
|
535
|
-
data-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
472
|
+
data-front-chat-id={config.integrations.support.front?.chatId}
|
|
473
|
+
set:html={frontBootstrap}
|
|
474
|
+
></script>
|
|
475
|
+
</>
|
|
476
|
+
)}
|
|
477
|
+
{chatwoot && (
|
|
478
|
+
<script
|
|
479
|
+
is:inline
|
|
480
|
+
data-chatwoot-base-url={config.integrations.support.chatwoot?.baseUrl}
|
|
481
|
+
data-chatwoot-website-token={
|
|
482
|
+
config.integrations.support.chatwoot?.websiteToken
|
|
483
|
+
}
|
|
484
|
+
set:html={chatwoot}
|
|
485
|
+
></script>
|
|
486
|
+
)}
|
|
487
|
+
{zammad && zammadBootstrap && (
|
|
488
|
+
<>
|
|
489
|
+
<script is:inline src={zammad.src}></script>
|
|
490
|
+
<script
|
|
491
|
+
is:inline
|
|
492
|
+
data-zammad-chat-id={config.integrations.support.zammad?.chatId}
|
|
493
|
+
set:html={zammadBootstrap}
|
|
494
|
+
></script>
|
|
495
|
+
</>
|
|
496
|
+
)}
|
|
555
497
|
</body>
|
|
556
498
|
</html>
|
|
@@ -6,9 +6,17 @@ import { pages as markdownPages } from "virtual:heyo-docs-content/server";
|
|
|
6
6
|
|
|
7
7
|
export const prerender = false;
|
|
8
8
|
|
|
9
|
-
export const POST: APIRoute = ({ request }) =>
|
|
10
|
-
|
|
9
|
+
export const POST: APIRoute = ({ request }) => {
|
|
10
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
11
|
+
if (!apiKey)
|
|
12
|
+
return Response.json(
|
|
13
|
+
{ error: "OPENAI_API_KEY is not configured." },
|
|
14
|
+
{ status: 500 },
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return createAiChatResponse(request, {
|
|
11
18
|
ai: config.ai,
|
|
19
|
+
auth: { type: "api-key", token: apiKey },
|
|
12
20
|
markdownPages,
|
|
13
21
|
pages: markdownPages.map((page) => ({
|
|
14
22
|
description: page.description,
|
|
@@ -18,3 +26,4 @@ export const POST: APIRoute = ({ request }) =>
|
|
|
18
26
|
})),
|
|
19
27
|
title: config.title,
|
|
20
28
|
});
|
|
29
|
+
};
|
|
@@ -6,8 +6,16 @@ import { docsPages, markdownPages } from "../../_heyo-docs/server";
|
|
|
6
6
|
export const runtime = "nodejs";
|
|
7
7
|
|
|
8
8
|
export async function POST(request: Request) {
|
|
9
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
10
|
+
if (!apiKey)
|
|
11
|
+
return Response.json(
|
|
12
|
+
{ error: "OPENAI_API_KEY is not configured." },
|
|
13
|
+
{ status: 500 },
|
|
14
|
+
);
|
|
15
|
+
|
|
9
16
|
return createAiChatResponse(request, {
|
|
10
17
|
ai: config.ai,
|
|
18
|
+
auth: { type: "api-key", token: apiKey },
|
|
11
19
|
markdownPages,
|
|
12
20
|
pages: docsPages,
|
|
13
21
|
title: config.title,
|
|
@@ -66,15 +66,15 @@ sidebar entry should point to a page outside the local documentation.
|
|
|
66
66
|
|
|
67
67
|
## AI chat
|
|
68
68
|
|
|
69
|
-
Add `ai.chat` to enable a documentation-aware chat
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
Add `ai.chat` to enable a documentation-aware chat through [Pi](https://github.com/badlogic/pi-mono).
|
|
70
|
+
Use any provider identifier bundled with the installed Pi version, such as
|
|
71
|
+
`openai`, `anthropic`, `google`, `openrouter`, or `xai`. Authentication stays
|
|
72
|
+
server-only and is supplied by the route that handles the chat request.
|
|
72
73
|
|
|
73
74
|
<CodeBlock language="ts" title="heyo-docs.config.ts">
|
|
74
75
|
{`ai: {
|
|
75
76
|
chat: {
|
|
76
77
|
provider: "openai",
|
|
77
|
-
key: process.env.OPENAI_API_KEY!,
|
|
78
78
|
model: "gpt-5-mini",
|
|
79
79
|
variant: "right", // "center" shows the compact prompt first
|
|
80
80
|
icon: "chat",
|
|
@@ -85,8 +85,15 @@ server environment variable; Heyo Docs sends it only from the chat endpoint.
|
|
|
85
85
|
},`}
|
|
86
86
|
</CodeBlock>
|
|
87
87
|
|
|
88
|
-
`provider
|
|
89
|
-
|
|
88
|
+
`provider` and `model` are required. Keep the credential out of this
|
|
89
|
+
configuration and pass it as `auth` to `createAiChatResponse` in the server
|
|
90
|
+
route. Cloudflare routes obtain the key from the Worker `env` binding; Node.js
|
|
91
|
+
routes can read their server environment there. GitHub Copilot and OpenAI Codex
|
|
92
|
+
use `auth: { type: "oauth", getAccessToken }`; Amazon Bedrock uses either
|
|
93
|
+
`{ type: "aws", region?, profile? }` and the AWS credential chain, or
|
|
94
|
+
`{ type: "bedrock-bearer", token, region? }`. The model must be an actual model
|
|
95
|
+
identifier, not the provider name. Amazon Bedrock requires a Node.js chat
|
|
96
|
+
endpoint. `variant` defaults to `right`. `name`
|
|
90
97
|
defaults to `AI`; it is used for the drawer title and the assistant's message
|
|
91
98
|
label. `placeholder` defaults to
|
|
92
99
|
`Ask AI about the docs` and is shared by the compact prompt and drawer input.
|
|
@@ -30,6 +30,19 @@ the sitemap, RSS feed, robots file, and AI-facing documentation endpoints.
|
|
|
30
30
|
Use the equivalent `npm run build`, `pnpm build`, or `yarn build` command
|
|
31
31
|
when the project uses a different package manager.
|
|
32
32
|
|
|
33
|
+
## Cloudflare secrets
|
|
34
|
+
|
|
35
|
+
For AI chat, save the provider key as a Worker secret:
|
|
36
|
+
|
|
37
|
+
<CodeBlock language="bash" title="Terminal">
|
|
38
|
+
{"bunx wrangler secret put OPENAI_API_KEY"}
|
|
39
|
+
</CodeBlock>
|
|
40
|
+
|
|
41
|
+
Keep the credential out of `heyo-docs.config.ts`. The Cloudflare overlay reads
|
|
42
|
+
the native Worker `env.OPENAI_API_KEY` binding in the AI route, after the
|
|
43
|
+
request reaches the Worker. It is never part of the documentation build or the
|
|
44
|
+
browser bundle.
|
|
45
|
+
|
|
33
46
|
<Callout type="tip" title="Set the canonical URL">
|
|
34
47
|
Add the final public origin as <code>siteUrl</code> in
|
|
35
48
|
<code>heyo-docs.config.ts</code> before the first production release. It keeps
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createAiChatResponse } from "@heyo-sh/heyo-docs/ai";
|
|
2
1
|
import type { ActionFunctionArgs } from "react-router";
|
|
3
2
|
|
|
4
3
|
import config from "../../heyo-docs.config";
|
|
@@ -12,8 +11,17 @@ export async function action({ request }: ActionFunctionArgs) {
|
|
|
12
11
|
status: 405,
|
|
13
12
|
});
|
|
14
13
|
|
|
14
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
15
|
+
if (!apiKey)
|
|
16
|
+
return Response.json(
|
|
17
|
+
{ error: "OPENAI_API_KEY is not configured." },
|
|
18
|
+
{ status: 500 },
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const { createAiChatResponse } = await import("@heyo-sh/heyo-docs/ai");
|
|
15
22
|
return createAiChatResponse(request, {
|
|
16
23
|
ai: config.ai,
|
|
24
|
+
auth: { type: "api-key", token: apiKey },
|
|
17
25
|
markdownPages,
|
|
18
26
|
pages,
|
|
19
27
|
title: config.title,
|
|
@@ -66,15 +66,15 @@ sidebar entry should point to a page outside the local documentation.
|
|
|
66
66
|
|
|
67
67
|
## AI chat
|
|
68
68
|
|
|
69
|
-
Add `ai.chat` to enable a documentation-aware chat
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
Add `ai.chat` to enable a documentation-aware chat through [Pi](https://github.com/badlogic/pi-mono).
|
|
70
|
+
Use any provider identifier bundled with the installed Pi version, such as
|
|
71
|
+
`openai`, `anthropic`, `google`, `openrouter`, or `xai`. Authentication stays
|
|
72
|
+
server-only and is supplied by the route that handles the chat request.
|
|
72
73
|
|
|
73
74
|
<CodeBlock language="ts" title="heyo-docs.config.ts">
|
|
74
75
|
{`ai: {
|
|
75
76
|
chat: {
|
|
76
77
|
provider: "openai",
|
|
77
|
-
key: process.env.OPENAI_API_KEY!,
|
|
78
78
|
model: "gpt-5-mini",
|
|
79
79
|
variant: "right", // "center" shows the compact prompt first
|
|
80
80
|
icon: "chat",
|
|
@@ -85,8 +85,15 @@ server environment variable; Heyo Docs sends it only from the chat endpoint.
|
|
|
85
85
|
},`}
|
|
86
86
|
</CodeBlock>
|
|
87
87
|
|
|
88
|
-
`provider
|
|
89
|
-
|
|
88
|
+
`provider` and `model` are required. Keep the credential out of this
|
|
89
|
+
configuration and pass it as `auth` to `createAiChatResponse` in the server
|
|
90
|
+
route. Cloudflare routes obtain the key from the Worker `env` binding; Node.js
|
|
91
|
+
routes can read their server environment there. GitHub Copilot and OpenAI Codex
|
|
92
|
+
use `auth: { type: "oauth", getAccessToken }`; Amazon Bedrock uses either
|
|
93
|
+
`{ type: "aws", region?, profile? }` and the AWS credential chain, or
|
|
94
|
+
`{ type: "bedrock-bearer", token, region? }`. The model must be an actual model
|
|
95
|
+
identifier, not the provider name. Amazon Bedrock requires a Node.js chat
|
|
96
|
+
endpoint. `variant` defaults to `right`. `name`
|
|
90
97
|
defaults to `AI`; it is used for the drawer title and the assistant's message
|
|
91
98
|
label. `placeholder` defaults to
|
|
92
99
|
`Ask AI about the docs` and is shared by the compact prompt and drawer input.
|
|
@@ -30,6 +30,19 @@ the sitemap, RSS feed, robots file, and AI-facing documentation endpoints.
|
|
|
30
30
|
Use the equivalent `npm run build`, `pnpm build`, or `yarn build` command
|
|
31
31
|
when the project uses a different package manager.
|
|
32
32
|
|
|
33
|
+
## Cloudflare secrets
|
|
34
|
+
|
|
35
|
+
For AI chat, save the provider key as a Worker secret:
|
|
36
|
+
|
|
37
|
+
<CodeBlock language="bash" title="Terminal">
|
|
38
|
+
{"bunx wrangler secret put OPENAI_API_KEY"}
|
|
39
|
+
</CodeBlock>
|
|
40
|
+
|
|
41
|
+
Keep the credential out of `heyo-docs.config.ts`. The Cloudflare overlay reads
|
|
42
|
+
the native Worker `env.OPENAI_API_KEY` binding in the AI route, after the
|
|
43
|
+
request reaches the Worker. It is never part of the documentation build or the
|
|
44
|
+
browser bundle.
|
|
45
|
+
|
|
33
46
|
<Callout type="tip" title="Set the canonical URL">
|
|
34
47
|
Add the final public origin as <code>siteUrl</code> in
|
|
35
48
|
<code>heyo-docs.config.ts</code> before the first production release. It keeps
|