@piagent/platform 1.41.0 → 1.42.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/CHANGELOG.md +1006 -2038
- package/CONTRIBUTING.md +2 -0
- package/README.md +128 -325
- package/README.vi.md +149 -143
- package/docs/command-reference-vietnamese.md +2 -2
- package/docs/distribution-standard.md +6 -6
- package/docs/languages.json +2 -2
- package/docs/operator-manual-vietnamese.md +7 -7
- package/docs/publishing-for-teams.md +1 -0
- package/docs/release-install-policy.md +3 -3
- package/docs/team-onboarding.md +2 -2
- package/docs-site/architecture.html +71 -63
- package/docs-site/assets/docs.css +2736 -1030
- package/docs-site/assets/docs.js +451 -43
- package/docs-site/assets/og.png +0 -0
- package/docs-site/assets/piagent-logo.svg +9 -31
- package/docs-site/assets/search-en.json +1 -0
- package/docs-site/assets/search-vi.json +1 -0
- package/docs-site/benchmark.html +71 -63
- package/docs-site/commands.html +72 -64
- package/docs-site/content/en/index.html +220 -131
- package/docs-site/content/en/quickstart.html +74 -90
- package/docs-site/content/en/update.html +80 -0
- package/docs-site/content/en/whats-new.html +1 -684
- package/docs-site/content/index.html +220 -131
- package/docs-site/content/quickstart.html +73 -108
- package/docs-site/content/update.html +80 -0
- package/docs-site/content/whats-new.html +1 -684
- package/docs-site/context-engine.html +73 -65
- package/docs-site/ecosystem.html +73 -65
- package/docs-site/en/architecture.html +67 -59
- package/docs-site/en/benchmark.html +67 -59
- package/docs-site/en/commands.html +68 -60
- package/docs-site/en/context-engine.html +68 -60
- package/docs-site/en/ecosystem.html +68 -60
- package/docs-site/en/index.html +281 -220
- package/docs-site/en/mcp-approval.html +67 -59
- package/docs-site/en/mcp-auth.html +67 -59
- package/docs-site/en/mcp-servers.html +68 -60
- package/docs-site/en/mcp.html +64 -56
- package/docs-site/en/permissions.html +68 -60
- package/docs-site/en/profiles.html +68 -60
- package/docs-site/en/quickstart.html +142 -150
- package/docs-site/en/runtime.html +67 -59
- package/docs-site/en/scope.html +68 -60
- package/docs-site/en/security.html +64 -56
- package/docs-site/en/team.html +67 -59
- package/docs-site/en/update.html +228 -0
- package/docs-site/en/whats-new.html +1601 -725
- package/docs-site/en/windows.html +71 -63
- package/docs-site/en/workflows.html +68 -60
- package/docs-site/favicon.svg +9 -14
- package/docs-site/index.html +281 -220
- package/docs-site/mcp-approval.html +70 -62
- package/docs-site/mcp-auth.html +70 -62
- package/docs-site/mcp-servers.html +71 -63
- package/docs-site/mcp.html +70 -62
- package/docs-site/permissions.html +72 -64
- package/docs-site/profiles.html +72 -64
- package/docs-site/quickstart.html +145 -172
- package/docs-site/releases.json +4449 -0
- package/docs-site/runtime.html +73 -65
- package/docs-site/scope.html +73 -65
- package/docs-site/security.html +73 -65
- package/docs-site/team.html +71 -63
- package/docs-site/update.html +228 -0
- package/docs-site/whats-new.html +1605 -729
- package/docs-site/windows.html +75 -67
- package/docs-site/workflows.html +73 -65
- package/evals/private-holdout-v1/public-exposure.v2.json +1 -1
- package/package.json +1 -1
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-webui/dist/client/assets/App-DBx4BU3F.js +5 -0
- package/packages/piagent-webui/dist/client/assets/LineReview-DzonwD-g.js +3 -0
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-DeLo-xiz.js → MarkdownMessage-DchODuO7.js} +2 -2
- package/packages/piagent-webui/dist/client/assets/SessionInspectorDrawer-BHHNlct3.js +1 -0
- package/packages/piagent-webui/dist/client/assets/SettingsPage-DX28MsZA.js +1 -0
- package/packages/piagent-webui/dist/client/assets/TerminalPanel-C--BiN0p.js +2 -0
- package/packages/piagent-webui/dist/client/assets/{TerminalView-r9rG24IO.js → TerminalView-BML7uS6R.js} +3 -3
- package/packages/piagent-webui/dist/client/assets/WorkspaceCodeEditor-B7ieqDnt.js +2 -0
- package/packages/piagent-webui/dist/client/assets/WorkspaceExplorer-BOafuPU-.js +5 -0
- package/packages/piagent-webui/dist/client/assets/WorkspaceQuickOpen-CGoGfUPk.js +1 -0
- package/packages/piagent-webui/dist/client/assets/api-BUjrtT1B.js +1 -0
- package/packages/piagent-webui/dist/client/assets/index-C7Azrwpq.js +132 -0
- package/packages/piagent-webui/dist/client/assets/index-Dqb2hNan.css +1 -0
- package/packages/piagent-webui/dist/client/assets/tab-close-Crj8vsUq.js +1 -0
- package/packages/piagent-webui/dist/client/assets/ui-preferences-B3UfDp-5.js +1 -0
- package/packages/piagent-webui/dist/client/assets/vendor-Ch5hIe0W.js +136 -0
- package/packages/piagent-webui/dist/client/index.html +5 -8
- package/scripts/build-docs-site.mjs +205 -348
- package/scripts/check-public-wording.mjs +2 -1
- package/scripts/docs-site-icons.mjs +84 -0
- package/scripts/docs-site-nav.mjs +302 -0
- package/scripts/docs-site-releases.mjs +227 -0
- package/packages/piagent-webui/dist/client/assets/Alert-DGWhP2MM.js +0 -1
- package/packages/piagent-webui/dist/client/assets/Button-QItfZUNj.js +0 -1
- package/packages/piagent-webui/dist/client/assets/DialogTitle-CyaFPaiW.js +0 -3
- package/packages/piagent-webui/dist/client/assets/TerminalPanel-BG-DP_qm.js +0 -2
- package/packages/piagent-webui/dist/client/assets/Typography-B9om7Evq.js +0 -1
- package/packages/piagent-webui/dist/client/assets/WorkspaceCodeEditor-BnqaR3pK.js +0 -2
- package/packages/piagent-webui/dist/client/assets/WorkspaceExplorer-BIxwiGtl.js +0 -5
- package/packages/piagent-webui/dist/client/assets/WorkspaceQuickOpen-CK3dILhQ.js +0 -1
- package/packages/piagent-webui/dist/client/assets/index-DBUTrEXi.css +0 -1
- package/packages/piagent-webui/dist/client/assets/index-rlyRccut.js +0 -102
- package/packages/piagent-webui/dist/client/assets/tab-close-BoJRyBO_.js +0 -1
- package/packages/piagent-webui/dist/client/assets/ui-preferences-BO3AV7QE.js +0 -10
- package/packages/piagent-webui/dist/client/assets/useId-DEefKUAT.js +0 -124
|
@@ -3,6 +3,10 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
|
+
import { icon, iconSprite, iconsUsedIn } from "./docs-site-icons.mjs";
|
|
7
|
+
import { NAV, NAV_BY_LOCALE } from "./docs-site-nav.mjs";
|
|
8
|
+
import { readReleases, renderChangelogMarkdown, renderHeroPill, renderLatestReleases, renderReleaseNotes } from "./docs-site-releases.mjs";
|
|
9
|
+
|
|
6
10
|
// Assembles the docs site: one static page per topic, from a shared shell and a
|
|
7
11
|
// content fragment each.
|
|
8
12
|
//
|
|
@@ -12,15 +16,20 @@ import { fileURLToPath } from "node:url";
|
|
|
12
16
|
// here instead and the output is committed. `--check` fails when the committed
|
|
13
17
|
// output no longer matches its source, the same arrangement the capability
|
|
14
18
|
// catalog already uses.
|
|
19
|
+
//
|
|
20
|
+
// Release notes have one source, docs-site/releases.json. It produces the
|
|
21
|
+
// Changelog page in both languages, the "latest" cards on the landing page and
|
|
22
|
+
// CHANGELOG.md, so the three can never disagree.
|
|
15
23
|
|
|
16
24
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
25
|
const siteRoot = path.join(repoRoot, "docs-site");
|
|
18
26
|
const contentRoot = path.join(siteRoot, "content");
|
|
19
27
|
|
|
20
28
|
const SITE = {
|
|
21
|
-
title: "Pi Agent Platform
|
|
29
|
+
title: "Pi Agent Platform",
|
|
22
30
|
origin: "https://piagent.io.vn",
|
|
23
31
|
repo: "https://github.com/Vt-mmm/piagent",
|
|
32
|
+
npm: "https://www.npmjs.com/package/@piagent/platform",
|
|
24
33
|
facebook: "https://www.facebook.com/vinhtam0544/"
|
|
25
34
|
};
|
|
26
35
|
|
|
@@ -44,262 +53,8 @@ const ANALYTICS_SNIPPET = ` <script>
|
|
|
44
53
|
<script defer src="/_vercel/insights/script.js"></script>
|
|
45
54
|
`;
|
|
46
55
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
const NAV = [
|
|
50
|
-
{
|
|
51
|
-
group: "Get started",
|
|
52
|
-
pages: [
|
|
53
|
-
{
|
|
54
|
-
slug: "index",
|
|
55
|
-
href: "/",
|
|
56
|
-
nav: "Overview",
|
|
57
|
-
title: "Pi Agent Platform",
|
|
58
|
-
lead: "Coding agent cho cả team: một dashboard cho Terminal và trình duyệt, model cá nhân hoặc của công ty, guard kiểm tra mọi tool call."
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
slug: "ecosystem",
|
|
62
|
-
nav: "Hệ sinh thái",
|
|
63
|
-
title: "Piagent, Agent Watch và Agent Studio",
|
|
64
|
-
lead: "Một dashboard, hai cách dùng model: tài khoản của bạn, hoặc model của công ty qua Agent Watch và Agent Studio, với Harness tự kiểm tra từng lượt."
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
slug: "quickstart",
|
|
68
|
-
nav: "Quickstart",
|
|
69
|
-
title: "Step by step cho thành viên mới",
|
|
70
|
-
lead: "Hai lệnh để cài, mở repo, chọn profile và quyền, rồi giao việc bằng lời thường; guard kiểm tra từng tool call."
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
slug: "windows",
|
|
74
|
-
nav: "Cài trên Windows",
|
|
75
|
-
title: "Piagent trên Windows bằng một lệnh",
|
|
76
|
-
lead: "Một lệnh PowerShell cài WSL2, Ubuntu, Piagent và Agent Watch, nối key công ty và thêm Piagent vào Start menu; chạy lại để cập nhật."
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
slug: "whats-new",
|
|
80
|
-
nav: "Có gì mới",
|
|
81
|
-
title: "Bản 1.9.0: nói việc bằng lời, Harness của team kiểm tra",
|
|
82
|
-
lead: "Bỏ task contract và lệnh workflow. Chế độ công ty tự yêu cầu check và review; nút Dừng, lệnh có mạng theo lần duyệt và thẻ duyệt dễ đọc hơn."
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
slug: "scope",
|
|
86
|
-
nav: "Phạm vi platform",
|
|
87
|
-
title: "Platform này nhận phần nào về mình",
|
|
88
|
-
lead: "Agent coding CLI nào cũng đã có cơ chế governance riêng. Platform này không thay thế lớp đó — nó đóng gói một cách vận hành cụ thể trên Pi để mọi repo dùng lại."
|
|
89
|
-
}
|
|
90
|
-
]
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
group: "Build",
|
|
94
|
-
pages: [
|
|
95
|
-
{
|
|
96
|
-
slug: "profiles",
|
|
97
|
-
nav: "Project profile",
|
|
98
|
-
title: "Chọn đúng đường ray cho repo",
|
|
99
|
-
lead: "Profile ghi vào .pi/piagent-profile.json và lock tương ứng. Nó quyết định protected path, verify command và capability mà project cấp."
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
slug: "permissions",
|
|
103
|
-
nav: "Permission modes",
|
|
104
|
-
title: "Một command để đổi quyền theo task",
|
|
105
|
-
lead: "Permission mode là session-local override. Đổi mode không sửa project profile."
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
slug: "commands",
|
|
109
|
-
nav: "Command catalog",
|
|
110
|
-
title: "Command ngắn, rõ, ít token burn",
|
|
111
|
-
lead: "Slash command gói sẵn policy và flow; một inspector menu duy nhất cho diff, command, safety và context. Thành viên không cần nhớ nhiều command rời."
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
slug: "workflows",
|
|
115
|
-
nav: "Workflows",
|
|
116
|
-
title: "Workflow thay đổi thế nào sau update",
|
|
117
|
-
lead: "Từ bản 1.9.0 không còn launcher workflow: nói việc cần làm bằng lời thường. Các command xem thêm chỉ để quan sát và không bao giờ mở lượt model."
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
group: "MCP",
|
|
123
|
-
pages: [
|
|
124
|
-
{
|
|
125
|
-
slug: "mcp",
|
|
126
|
-
nav: "MCP overview",
|
|
127
|
-
title: "MCP trong Pi Agent Platform",
|
|
128
|
-
lead: "Pi core không hard-code MCP. Platform cài pi-mcp-adapter để dùng MCP theo kiểu token-efficient, và thêm một lớp quản lý server lên trên bốn layer config."
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
slug: "mcp-servers",
|
|
132
|
-
nav: "Quản lý server",
|
|
133
|
-
title: "Thêm, xem và gỡ MCP server",
|
|
134
|
-
lead: "piagent-mcp có bề mặt lệnh đầy đủ chứ không chỉ seed preset: add, remove, get, list, enable, disable — trên cả bốn scope."
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
slug: "mcp-auth",
|
|
138
|
-
nav: "Auth và readiness",
|
|
139
|
-
title: "Đăng nhập và trạng thái sẵn sàng",
|
|
140
|
-
lead: "Platform không giữ token và không tự chạy OAuth. Nó từ chối ghi secret vào config, và cho biết server nào thật sự gọi được."
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
slug: "mcp-approval",
|
|
144
|
-
nav: "Approval gate",
|
|
145
|
-
title: "Server đến từ repo phải được duyệt",
|
|
146
|
-
lead: "Clone một repo lạ rồi mở session không được phép cho tác giả repo đó một process trên máy mình. Quyết định lưu ngoài repo và pin theo digest."
|
|
147
|
-
}
|
|
148
|
-
]
|
|
149
|
-
},
|
|
150
|
-
{
|
|
151
|
-
group: "Operate",
|
|
152
|
-
pages: [
|
|
153
|
-
{
|
|
154
|
-
slug: "security",
|
|
155
|
-
nav: "Security model",
|
|
156
|
-
title: "Policy enforcement layer, không phải OS sandbox",
|
|
157
|
-
lead: "Platform ưu tiên fail-closed trong bề mặt tool được kiểm soát: khi lock sai, profile sai hoặc quyền không đủ thì chặn, không đoán."
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
slug: "architecture",
|
|
161
|
-
nav: "Architecture",
|
|
162
|
-
title: "Kiến trúc repo",
|
|
163
|
-
lead: "Repo này không nhét business logic vào core. Core chỉ giữ policy, prompts, guard, skills và scripts dùng lại được."
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
slug: "context-engine",
|
|
167
|
-
nav: "Context Engine",
|
|
168
|
-
title: "Ít context hơn, giữ nguyên chất lượng model",
|
|
169
|
-
lead: "Index local, retrieval có xếp hạng, tool loading theo task, semantic compaction và telemetry minh bạch cho Agent Watch."
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
slug: "benchmark",
|
|
173
|
-
nav: "Benchmark",
|
|
174
|
-
title: "Bằng chứng chất lượng và hiệu suất",
|
|
175
|
-
lead: "Production benchmark so sánh Piagent với surface codex-cli trên cùng model, cùng thinking level, 18 nhóm bài và 108 session được chấm tự động."
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
slug: "runtime",
|
|
179
|
-
nav: "Runtime tools",
|
|
180
|
-
title: "Terminal commands cho operator",
|
|
181
|
-
lead: "Nhóm command này chạy ngoài slash command, dùng cho setup, doctor, benchmark, capability và MCP."
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
slug: "team",
|
|
185
|
-
nav: "Team usage",
|
|
186
|
-
title: "Team nên dùng như một chuẩn vận hành",
|
|
187
|
-
lead: "Mỗi repo chỉ cần link package, chọn profile đúng và giữ tài liệu project-specific ở repo của dự án đó."
|
|
188
|
-
}
|
|
189
|
-
]
|
|
190
|
-
}
|
|
191
|
-
];
|
|
192
|
-
|
|
193
|
-
const EN_PAGE_COPY = {
|
|
194
|
-
index: {
|
|
195
|
-
nav: "Overview",
|
|
196
|
-
title: "Pi Agent Platform",
|
|
197
|
-
lead: "A coding agent for the whole team: one dashboard for Terminal and the browser, personal or company models, and a guard that checks every tool call."
|
|
198
|
-
},
|
|
199
|
-
ecosystem: {
|
|
200
|
-
nav: "Ecosystem",
|
|
201
|
-
title: "Piagent, Agent Watch and Agent Studio",
|
|
202
|
-
lead: "One dashboard, two ways to use models: your own account, or the company's models through Agent Watch and Agent Studio, with a Harness that checks every turn."
|
|
203
|
-
},
|
|
204
|
-
quickstart: {
|
|
205
|
-
nav: "Quickstart",
|
|
206
|
-
title: "Step by step for new team members",
|
|
207
|
-
lead: "Install with two commands, open a repository, choose its profile and permissions, then ask in plain language; the guard checks every tool call."
|
|
208
|
-
},
|
|
209
|
-
windows: {
|
|
210
|
-
nav: "Windows",
|
|
211
|
-
title: "Piagent on Windows with one command",
|
|
212
|
-
lead: "One PowerShell command installs WSL2, Ubuntu, Piagent and Agent Watch, binds the company key and adds Piagent to the Start menu; run it again to update."
|
|
213
|
-
},
|
|
214
|
-
"whats-new": {
|
|
215
|
-
nav: "What’s new",
|
|
216
|
-
title: "1.9.0: ask in plain language, the team's Harness checks",
|
|
217
|
-
lead: "The task contract and workflow commands are gone. Company mode asks for checks and a review on its own; Stop, network commands on approval and clearer approval cards."
|
|
218
|
-
},
|
|
219
|
-
scope: {
|
|
220
|
-
nav: "Platform scope",
|
|
221
|
-
title: "What the platform owns",
|
|
222
|
-
lead: "General coding agents already provide governance primitives. This platform packages one repeatable operating model for Pi across every repository."
|
|
223
|
-
},
|
|
224
|
-
profiles: {
|
|
225
|
-
nav: "Project profiles",
|
|
226
|
-
title: "Choose the right operating profile",
|
|
227
|
-
lead: "A project profile defines protected paths, verification commands, and the capabilities granted to a repository."
|
|
228
|
-
},
|
|
229
|
-
permissions: {
|
|
230
|
-
nav: "Permission modes",
|
|
231
|
-
title: "Change task permissions with one command",
|
|
232
|
-
lead: "Permission modes are session-local overrides. Changing a mode does not modify the project profile."
|
|
233
|
-
},
|
|
234
|
-
commands: {
|
|
235
|
-
nav: "Command catalog",
|
|
236
|
-
title: "Short commands with predictable behavior",
|
|
237
|
-
lead: "Slash commands package policy and workflow directly, with one inspector menu for diffs, commands, safety, and context instead of many one-off commands."
|
|
238
|
-
},
|
|
239
|
-
workflows: {
|
|
240
|
-
nav: "Workflows",
|
|
241
|
-
title: "How workflows change after the update",
|
|
242
|
-
lead: "Since 1.9.0 there are no workflow launchers: say what you need in plain language. The optional inspection commands only observe and never start a model turn."
|
|
243
|
-
},
|
|
244
|
-
mcp: {
|
|
245
|
-
nav: "MCP overview",
|
|
246
|
-
title: "MCP in Pi Agent Platform",
|
|
247
|
-
lead: "The platform installs pi-mcp-adapter for token-efficient MCP access and manages servers across four configuration layers."
|
|
248
|
-
},
|
|
249
|
-
"mcp-servers": {
|
|
250
|
-
nav: "Server management",
|
|
251
|
-
title: "Add, inspect, and remove MCP servers",
|
|
252
|
-
lead: "piagent-mcp provides deterministic add, remove, get, list, enable, and disable operations across all four scopes."
|
|
253
|
-
},
|
|
254
|
-
"mcp-auth": {
|
|
255
|
-
nav: "Authentication",
|
|
256
|
-
title: "Authentication and readiness",
|
|
257
|
-
lead: "The platform does not store tokens or run OAuth. It rejects secrets in configuration and reports which servers are actually ready."
|
|
258
|
-
},
|
|
259
|
-
"mcp-approval": {
|
|
260
|
-
nav: "Approval gate",
|
|
261
|
-
title: "Repository servers require approval",
|
|
262
|
-
lead: "Cloning an untrusted repository must not grant its author a process on the operator's machine. Decisions live outside the repository and are pinned by digest."
|
|
263
|
-
},
|
|
264
|
-
security: {
|
|
265
|
-
nav: "Security model",
|
|
266
|
-
title: "Policy enforcement, not an OS sandbox",
|
|
267
|
-
lead: "Controlled tool surfaces fail closed when profiles, integrity locks or permissions are invalid."
|
|
268
|
-
},
|
|
269
|
-
architecture: {
|
|
270
|
-
nav: "Architecture",
|
|
271
|
-
title: "Repository architecture",
|
|
272
|
-
lead: "Core contains reusable policy and runtime infrastructure, while project-specific business logic stays in the project that owns it."
|
|
273
|
-
},
|
|
274
|
-
"context-engine": {
|
|
275
|
-
nav: "Context Engine",
|
|
276
|
-
title: "Less context without reducing model quality",
|
|
277
|
-
lead: "Local indexing, ranked retrieval, task-driven tool loading, semantic compaction, and transparent telemetry for Agent Watch."
|
|
278
|
-
},
|
|
279
|
-
benchmark: {
|
|
280
|
-
nav: "Benchmark",
|
|
281
|
-
title: "Quality and efficiency evidence",
|
|
282
|
-
lead: "The production benchmark compares Piagent with codex-cli using the same model, thinking level, 18 task families, and 108 automatically graded sessions."
|
|
283
|
-
},
|
|
284
|
-
runtime: {
|
|
285
|
-
nav: "Runtime tools",
|
|
286
|
-
title: "Terminal commands for operators",
|
|
287
|
-
lead: "Use these commands outside Pi for setup, updates, doctor checks, benchmarks, capabilities, and MCP management."
|
|
288
|
-
},
|
|
289
|
-
team: {
|
|
290
|
-
nav: "Team usage",
|
|
291
|
-
title: "A repeatable team operating standard",
|
|
292
|
-
lead: "Each repository links the package, selects a fitting profile, and keeps project-specific documentation in the repository that owns it."
|
|
293
|
-
}
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
const NAV_BY_LOCALE = {
|
|
297
|
-
vi: NAV,
|
|
298
|
-
en: NAV.map((section) => ({
|
|
299
|
-
group: section.group,
|
|
300
|
-
pages: section.pages.map((page) => ({ ...page, ...EN_PAGE_COPY[page.slug] }))
|
|
301
|
-
}))
|
|
302
|
-
};
|
|
56
|
+
// Set before first paint so a reader who chose light never sees a dark flash.
|
|
57
|
+
const THEME_SNIPPET = `<script>try{var t=localStorage.getItem("piagent-docs-theme");if(t==="light"||t==="dark")document.documentElement.dataset.theme=t}catch(e){}</script>`;
|
|
303
58
|
|
|
304
59
|
const LOCALES = {
|
|
305
60
|
vi: {
|
|
@@ -312,19 +67,28 @@ const LOCALES = {
|
|
|
312
67
|
breadcrumb: "Docs",
|
|
313
68
|
language: "Ngôn ngữ",
|
|
314
69
|
githubAria: "Mở GitHub repository",
|
|
315
|
-
menu: "
|
|
316
|
-
|
|
70
|
+
menu: "Mở menu tài liệu",
|
|
71
|
+
docs: "Tài liệu",
|
|
72
|
+
changelog: "Changelog",
|
|
73
|
+
benchmark: "Benchmark",
|
|
317
74
|
searchAria: "Tìm trong tài liệu",
|
|
318
|
-
searchPlaceholder: "Tìm
|
|
75
|
+
searchPlaceholder: "Tìm trong tài liệu…",
|
|
76
|
+
searchEmpty: "Không có trang nào khớp.",
|
|
77
|
+
searchHint: ["↑↓ chọn", "↵ mở", "esc đóng"],
|
|
78
|
+
themeAria: "Đổi giao diện sáng/tối",
|
|
319
79
|
navAria: "Tài liệu",
|
|
320
80
|
pagerAria: "Trang kế tiếp",
|
|
321
81
|
previous: "Trước",
|
|
322
82
|
next: "Tiếp",
|
|
323
83
|
tocAria: "Mục lục trang",
|
|
324
84
|
tocTitle: "Trên trang này",
|
|
85
|
+
editPage: "Sửa trang này",
|
|
325
86
|
externalLinks: "Liên kết ngoài",
|
|
87
|
+
skip: "Tới nội dung",
|
|
326
88
|
copySuccess: "Đã lấy",
|
|
327
|
-
copyFallback: "Chọn lệnh"
|
|
89
|
+
copyFallback: "Chọn lệnh",
|
|
90
|
+
copyLabel: "Sao chép",
|
|
91
|
+
footer: "Pi Agent Platform · MIT · tài liệu tĩnh, không cookie"
|
|
328
92
|
}
|
|
329
93
|
},
|
|
330
94
|
en: {
|
|
@@ -337,19 +101,28 @@ const LOCALES = {
|
|
|
337
101
|
breadcrumb: "Docs",
|
|
338
102
|
language: "Language",
|
|
339
103
|
githubAria: "Open GitHub repository",
|
|
340
|
-
menu: "
|
|
341
|
-
|
|
104
|
+
menu: "Open docs menu",
|
|
105
|
+
docs: "Docs",
|
|
106
|
+
changelog: "Changelog",
|
|
107
|
+
benchmark: "Benchmark",
|
|
342
108
|
searchAria: "Search documentation",
|
|
343
|
-
searchPlaceholder: "Search
|
|
109
|
+
searchPlaceholder: "Search the docs…",
|
|
110
|
+
searchEmpty: "No page matches.",
|
|
111
|
+
searchHint: ["↑↓ select", "↵ open", "esc close"],
|
|
112
|
+
themeAria: "Switch light/dark theme",
|
|
344
113
|
navAria: "Documentation",
|
|
345
114
|
pagerAria: "Adjacent pages",
|
|
346
115
|
previous: "Previous",
|
|
347
116
|
next: "Next",
|
|
348
117
|
tocAria: "Page contents",
|
|
349
118
|
tocTitle: "On this page",
|
|
119
|
+
editPage: "Edit this page",
|
|
350
120
|
externalLinks: "External links",
|
|
121
|
+
skip: "Skip to content",
|
|
351
122
|
copySuccess: "Copied",
|
|
352
|
-
copyFallback: "Select command"
|
|
123
|
+
copyFallback: "Select command",
|
|
124
|
+
copyLabel: "Copy",
|
|
125
|
+
footer: "Pi Agent Platform · MIT · static docs, no cookies"
|
|
353
126
|
}
|
|
354
127
|
}
|
|
355
128
|
};
|
|
@@ -396,6 +169,9 @@ function stripTags(value) {
|
|
|
396
169
|
return text;
|
|
397
170
|
}
|
|
398
171
|
|
|
172
|
+
const decodeEntities = (value) => value
|
|
173
|
+
.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/&/g, "&");
|
|
174
|
+
|
|
399
175
|
/** @param {string} currentSlug @param {typeof LOCALES.vi} locale @returns {string} */
|
|
400
176
|
function renderNav(currentSlug, locale) {
|
|
401
177
|
return locale.nav.map((section) => {
|
|
@@ -404,7 +180,7 @@ function renderNav(currentSlug, locale) {
|
|
|
404
180
|
const current = page.slug === currentSlug;
|
|
405
181
|
const classes = current ? "nav-link current" : "nav-link";
|
|
406
182
|
const aria = current ? ' aria-current="page"' : "";
|
|
407
|
-
return ` <a class="${classes}" href="${hrefFor(page, locale)}"${aria}>${escapeHtml(page.nav)}</a>`;
|
|
183
|
+
return ` <a class="${classes}" href="${hrefFor(page, locale)}"${aria}>${icon(page.icon)}<span>${escapeHtml(page.nav)}</span></a>`;
|
|
408
184
|
})
|
|
409
185
|
.join("\n");
|
|
410
186
|
return ` <div class="nav-group">\n <p class="nav-title">${escapeHtml(section.group)}</p>\n${links}\n </div>`;
|
|
@@ -413,27 +189,28 @@ function renderNav(currentSlug, locale) {
|
|
|
413
189
|
|
|
414
190
|
/** @param {ReturnType<typeof pagesFor>[number]} page @param {typeof LOCALES.vi} locale @returns {string} */
|
|
415
191
|
function renderBreadcrumb(page, locale) {
|
|
416
|
-
if (page.slug === "index") return "";
|
|
417
192
|
return [
|
|
418
193
|
' <nav class="breadcrumb" aria-label="Breadcrumb">',
|
|
419
194
|
` <a href="${hrefFor({ slug: "index", href: "/" }, locale)}">${locale.ui.breadcrumb}</a>`,
|
|
420
195
|
' <span aria-hidden="true">/</span>',
|
|
421
196
|
` <span>${escapeHtml(page.group)}</span>`,
|
|
422
|
-
' <span aria-hidden="true">/</span>',
|
|
423
|
-
` <span>${escapeHtml(page.nav)}</span>`,
|
|
424
197
|
" </nav>",
|
|
425
198
|
""
|
|
426
199
|
].join("\n");
|
|
427
200
|
}
|
|
428
201
|
|
|
429
|
-
/** @param {ReturnType<typeof pagesFor>[number]} page @returns {string} */
|
|
430
|
-
function renderPageHead(page) {
|
|
431
|
-
|
|
432
|
-
|
|
202
|
+
/** @param {ReturnType<typeof pagesFor>[number]} page @param {typeof LOCALES.vi} locale @returns {string} */
|
|
203
|
+
function renderPageHead(page, locale) {
|
|
204
|
+
const source = page.slug === "whats-new"
|
|
205
|
+
? "docs-site/releases.json"
|
|
206
|
+
: `docs-site/content/${locale.prefix ? "en/" : ""}${page.slug}.html`;
|
|
433
207
|
return [
|
|
434
208
|
' <div class="page-head">',
|
|
435
209
|
` <h1>${escapeHtml(page.title)}</h1>`,
|
|
436
210
|
` <p>${page.lead}</p>`,
|
|
211
|
+
' <div class="page-meta">',
|
|
212
|
+
` <a href="${SITE.repo}/blob/main/${source}" target="_blank" rel="noopener noreferrer">${icon("edit")}${locale.ui.editPage}</a>`,
|
|
213
|
+
" </div>",
|
|
437
214
|
" </div>",
|
|
438
215
|
""
|
|
439
216
|
].join("\n");
|
|
@@ -448,7 +225,7 @@ function renderPager(index, pages, locale) {
|
|
|
448
225
|
if (previous) {
|
|
449
226
|
parts.push(
|
|
450
227
|
` <a class="pager-prev" href="${hrefFor(previous, locale)}">`,
|
|
451
|
-
` <span class="pager-label"
|
|
228
|
+
` <span class="pager-label">← ${locale.ui.previous}</span>`,
|
|
452
229
|
` <span class="pager-title">${escapeHtml(previous.nav)}</span>`,
|
|
453
230
|
" </a>"
|
|
454
231
|
);
|
|
@@ -456,7 +233,7 @@ function renderPager(index, pages, locale) {
|
|
|
456
233
|
if (next) {
|
|
457
234
|
parts.push(
|
|
458
235
|
` <a class="pager-next" href="${hrefFor(next, locale)}">`,
|
|
459
|
-
` <span class="pager-label">${locale.ui.next}
|
|
236
|
+
` <span class="pager-label">${locale.ui.next} →</span>`,
|
|
460
237
|
` <span class="pager-title">${escapeHtml(next.nav)}</span>`,
|
|
461
238
|
" </a>"
|
|
462
239
|
);
|
|
@@ -465,6 +242,65 @@ function renderPager(index, pages, locale) {
|
|
|
465
242
|
return parts.join("\n");
|
|
466
243
|
}
|
|
467
244
|
|
|
245
|
+
function renderTopbar(page, locale, version, home) {
|
|
246
|
+
const languageLinks = Object.values(LOCALES).map((candidate) => {
|
|
247
|
+
const current = candidate.code === locale.code;
|
|
248
|
+
const classes = current ? "language-option current" : "language-option";
|
|
249
|
+
const aria = current ? ' aria-current="true"' : "";
|
|
250
|
+
return `<a class="${classes}" href="${hrefFor(page, candidate)}" hreflang="${candidate.code}" lang="${candidate.code}"${aria}>${candidate.label}</a>`;
|
|
251
|
+
}).join("");
|
|
252
|
+
const link = (slug, label) => {
|
|
253
|
+
const current = page.slug === slug ? ' aria-current="page"' : "";
|
|
254
|
+
return `<a href="${hrefFor({ slug }, locale)}"${current}>${label}</a>`;
|
|
255
|
+
};
|
|
256
|
+
const menu = home ? "" : `\n <button class="icon-button mobile-menu" type="button" data-menu aria-label="${locale.ui.menu}">${icon("menu")}</button>`;
|
|
257
|
+
return ` <header class="topbar">${menu}
|
|
258
|
+
<a class="brand" href="${hrefFor({ slug: "index", href: "/" }, locale)}" aria-label="Pi Agent Platform">
|
|
259
|
+
<span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" width="28" height="28" /></span>
|
|
260
|
+
<span class="brand-name"><b>Piagent</b> <span>Platform</span></span>
|
|
261
|
+
</a>
|
|
262
|
+
<a class="version-badge" href="${hrefFor({ slug: "whats-new" }, locale)}" data-docs-version="v${version}" title="v${version} docs · ${locale.label}"><span class="dot"></span>v${version}</a>
|
|
263
|
+
<nav class="top-nav" aria-label="${locale.ui.navAria}">
|
|
264
|
+
${link("quickstart", locale.ui.docs)}
|
|
265
|
+
${link("whats-new", locale.ui.changelog)}
|
|
266
|
+
${link("benchmark", locale.ui.benchmark)}
|
|
267
|
+
</nav>
|
|
268
|
+
<span class="top-spacer"></span>
|
|
269
|
+
<button class="search-trigger" type="button" data-search aria-label="${locale.ui.searchAria}">${icon("search")}<span>${locale.ui.searchPlaceholder}</span><kbd>⌘K</kbd></button>
|
|
270
|
+
<div class="top-actions">
|
|
271
|
+
<nav class="language-switch" aria-label="${locale.ui.language}">${languageLinks}</nav>
|
|
272
|
+
<button class="icon-button theme-toggle" type="button" data-theme-toggle aria-label="${locale.ui.themeAria}">${icon("moon", "icon icon-moon")}${icon("sun", "icon icon-sun")}</button>
|
|
273
|
+
<a class="icon-button" href="${SITE.repo}" target="_blank" rel="noopener noreferrer" aria-label="${locale.ui.githubAria}">${icon("github")}</a>
|
|
274
|
+
</div>
|
|
275
|
+
</header>
|
|
276
|
+
`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function renderPalette(locale) {
|
|
280
|
+
const hints = locale.ui.searchHint.map((hint) => `<span>${hint}</span>`).join("");
|
|
281
|
+
return ` <div class="palette" data-palette data-index="/assets/search-${locale.code}.json" data-empty="${locale.ui.searchEmpty}" role="dialog" aria-modal="true" aria-label="${locale.ui.searchAria}">
|
|
282
|
+
<div class="palette-box">
|
|
283
|
+
<label class="palette-input">${icon("search")}<input type="search" placeholder="${locale.ui.searchPlaceholder}" aria-label="${locale.ui.searchAria}" autocomplete="off" spellcheck="false" /></label>
|
|
284
|
+
<div class="palette-results" data-results role="listbox"></div>
|
|
285
|
+
<div class="palette-foot">${hints}</div>
|
|
286
|
+
</div>
|
|
287
|
+
</div>
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function renderFooter(page, locale) {
|
|
292
|
+
const other = locale.code === "vi" ? LOCALES.en : LOCALES.vi;
|
|
293
|
+
return `<footer class="footer">
|
|
294
|
+
<p>${locale.ui.footer}</p>
|
|
295
|
+
<div class="footer-links" aria-label="${locale.ui.externalLinks}">
|
|
296
|
+
<a href="${SITE.repo}" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
297
|
+
<a href="${SITE.npm}" target="_blank" rel="noopener noreferrer">npm</a>
|
|
298
|
+
<a href="${hrefFor(page, other)}" hreflang="${other.code}">${other.code === "en" ? "English" : "Tiếng Việt"}</a>
|
|
299
|
+
<a href="${SITE.facebook}" target="_blank" rel="noopener noreferrer">Facebook</a>
|
|
300
|
+
</div>
|
|
301
|
+
</footer>`;
|
|
302
|
+
}
|
|
303
|
+
|
|
468
304
|
/**
|
|
469
305
|
* @param {ReturnType<typeof pagesFor>[number]} page
|
|
470
306
|
* @param {number} index
|
|
@@ -475,101 +311,91 @@ function renderPager(index, pages, locale) {
|
|
|
475
311
|
* @returns {string}
|
|
476
312
|
*/
|
|
477
313
|
function renderPage(page, index, body, version, locale, pages) {
|
|
314
|
+
const home = page.slug === "index";
|
|
478
315
|
const canonical = `${SITE.origin}${hrefFor(page, locale)}`;
|
|
479
316
|
const alternateVi = `${SITE.origin}${hrefFor(page, LOCALES.vi)}`;
|
|
480
317
|
const alternateEn = `${SITE.origin}${hrefFor(page, LOCALES.en)}`;
|
|
481
|
-
const documentTitle =
|
|
318
|
+
const documentTitle = home ? `${SITE.title} — ${stripTags(page.lead).split(":")[0]}` : `${page.title} · ${SITE.title}`;
|
|
482
319
|
const description = stripTags(page.lead);
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
320
|
+
|
|
321
|
+
const main = home
|
|
322
|
+
? ` <main id="content" class="home-main" data-article>
|
|
323
|
+
${body}
|
|
324
|
+
</main>
|
|
325
|
+
<footer class="site-footer">
|
|
326
|
+
<div class="home-wrap">
|
|
327
|
+
<p>${locale.ui.footer}</p>
|
|
328
|
+
<nav aria-label="${locale.ui.externalLinks}">
|
|
329
|
+
<a href="${hrefFor({ slug: "quickstart" }, locale)}">${locale.ui.docs}</a>
|
|
330
|
+
<a href="${hrefFor({ slug: "whats-new" }, locale)}">${locale.ui.changelog}</a>
|
|
331
|
+
<a href="${SITE.repo}" target="_blank" rel="noopener noreferrer">GitHub</a>
|
|
332
|
+
<a href="${SITE.npm}" target="_blank" rel="noopener noreferrer">npm</a>
|
|
333
|
+
<a href="${hrefFor(page, locale.code === "vi" ? LOCALES.en : LOCALES.vi)}" hreflang="${locale.code === "vi" ? "en" : "vi"}">${locale.code === "vi" ? "English" : "Tiếng Việt"}</a>
|
|
334
|
+
</nav>
|
|
335
|
+
</div>
|
|
336
|
+
</footer>
|
|
337
|
+
`
|
|
338
|
+
: ` <div class="layout with-toc">
|
|
339
|
+
<aside class="sidebar" data-sidebar>
|
|
340
|
+
<nav aria-label="${locale.ui.navAria}">
|
|
341
|
+
${renderNav(page.slug, locale)}
|
|
342
|
+
</nav>
|
|
343
|
+
</aside>
|
|
344
|
+
|
|
345
|
+
<main id="content">
|
|
346
|
+
<div class="content-shell" data-article>
|
|
347
|
+
${renderBreadcrumb(page, locale)}${renderPageHead(page, locale)}${body}
|
|
348
|
+
${renderPager(index, pages, locale)}
|
|
349
|
+
${renderFooter(page, locale)}
|
|
350
|
+
</div>
|
|
351
|
+
</main>
|
|
352
|
+
|
|
353
|
+
<aside class="toc" aria-label="${locale.ui.tocAria}">
|
|
354
|
+
<p class="toc-title">${locale.ui.tocTitle}</p>
|
|
355
|
+
<nav data-toc></nav>
|
|
356
|
+
</aside>
|
|
357
|
+
</div>
|
|
358
|
+
`;
|
|
359
|
+
|
|
360
|
+
const shell = `${renderTopbar(page, locale, version, home)}
|
|
361
|
+
${main}${renderPalette(locale)}`;
|
|
362
|
+
const navIcons = pagesFor(locale).map((candidate) => candidate.icon);
|
|
363
|
+
const sprite = iconSprite([...new Set([...iconsUsedIn(shell), ...navIcons, "hash"])].sort());
|
|
489
364
|
|
|
490
365
|
return `<!doctype html>
|
|
491
|
-
<html lang="${locale.code}" data-copy-success="${locale.ui.copySuccess}" data-copy-fallback="${locale.ui.copyFallback}">
|
|
366
|
+
<html lang="${locale.code}" data-copy-success="${locale.ui.copySuccess}" data-copy-fallback="${locale.ui.copyFallback}" data-copy-label="${locale.ui.copyLabel}">
|
|
492
367
|
<head>
|
|
493
368
|
<meta charset="utf-8" />
|
|
494
369
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
495
370
|
<title>${escapeHtml(documentTitle)}</title>
|
|
496
371
|
<meta name="description" content="${escapeHtml(description)}" />
|
|
497
|
-
<meta name="theme-color" content="#
|
|
372
|
+
<meta name="theme-color" content="#09090b" media="(prefers-color-scheme: dark)" />
|
|
373
|
+
<meta name="theme-color" content="#fbfbf9" media="(prefers-color-scheme: light)" />
|
|
498
374
|
<link rel="canonical" href="${canonical}" />
|
|
499
375
|
<link rel="alternate" hreflang="vi" href="${alternateVi}" />
|
|
500
376
|
<link rel="alternate" hreflang="en" href="${alternateEn}" />
|
|
501
377
|
<link rel="alternate" hreflang="x-default" href="${alternateVi}" />
|
|
502
378
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
379
|
+
<meta property="og:type" content="website" />
|
|
503
380
|
<meta property="og:title" content="${escapeHtml(documentTitle)}" />
|
|
504
381
|
<meta property="og:description" content="${escapeHtml(description)}" />
|
|
505
382
|
<meta property="og:url" content="${canonical}" />
|
|
383
|
+
<meta property="og:image" content="${SITE.origin}/assets/og.png" />
|
|
384
|
+
<meta name="twitter:card" content="summary_large_image" />
|
|
385
|
+
${THEME_SNIPPET}
|
|
506
386
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
507
387
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
508
388
|
<link
|
|
509
|
-
href="https://fonts.googleapis.com/css2?family=
|
|
389
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;450;500;550;600;650;700;750&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
|
|
510
390
|
rel="stylesheet"
|
|
511
391
|
/>
|
|
512
392
|
<link rel="stylesheet" href="/assets/docs.css" />
|
|
513
393
|
<script src="/assets/docs.js" defer></script>
|
|
514
394
|
</head>
|
|
515
|
-
<body>
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
<span>Pi Agent Platform</span>
|
|
520
|
-
</a>
|
|
521
|
-
<div class="top-actions">
|
|
522
|
-
<span class="pill"><span class="pill-dot"></span>v${version} docs · ${locale.label}</span>
|
|
523
|
-
<nav class="language-switch" aria-label="${locale.ui.language}">
|
|
524
|
-
${languageLinks}
|
|
525
|
-
</nav>
|
|
526
|
-
<a class="button social" href="${SITE.repo}" target="_blank" rel="noopener noreferrer" aria-label="${locale.ui.githubAria}">
|
|
527
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
528
|
-
<path d="M7 17 17 7M9 7h8v8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
529
|
-
</svg>
|
|
530
|
-
GitHub
|
|
531
|
-
</a>
|
|
532
|
-
<button class="button mobile-menu" type="button" data-menu>${locale.ui.menu}</button>
|
|
533
|
-
<a class="button top-command-link" href="${hrefFor({ slug: "commands" }, locale)}">${locale.ui.commands}</a>
|
|
534
|
-
</div>
|
|
535
|
-
</header>
|
|
536
|
-
|
|
537
|
-
<div class="layout with-toc">
|
|
538
|
-
<aside class="sidebar" data-sidebar>
|
|
539
|
-
<div class="sidebar-search" aria-label="${locale.ui.searchAria}">
|
|
540
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
541
|
-
<path d="M10.8 18.1a7.3 7.3 0 1 1 0-14.6 7.3 7.3 0 0 1 0 14.6Zm5.3-1.7 4.4 4.4" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
|
542
|
-
</svg>
|
|
543
|
-
<input type="search" placeholder="${locale.ui.searchPlaceholder}" data-filter />
|
|
544
|
-
</div>
|
|
545
|
-
|
|
546
|
-
<nav aria-label="${locale.ui.navAria}">
|
|
547
|
-
${renderNav(page.slug, locale)}
|
|
548
|
-
</nav>
|
|
549
|
-
</aside>
|
|
550
|
-
|
|
551
|
-
<main>
|
|
552
|
-
<div class="content-shell" data-article>
|
|
553
|
-
${renderBreadcrumb(page, locale)}${renderPageHead(page)}${body}
|
|
554
|
-
${renderPager(index, pages, locale)}
|
|
555
|
-
<footer class="footer">
|
|
556
|
-
<p>Pi Agent Platform docs · static HTML · governed agent workflow</p>
|
|
557
|
-
<div class="footer-links" aria-label="${locale.ui.externalLinks}">
|
|
558
|
-
<a href="${SITE.origin}" target="_blank" rel="noopener noreferrer">piagent.io.vn</a>
|
|
559
|
-
<a href="${SITE.repo}" target="_blank" rel="noopener noreferrer">GitHub repository</a>
|
|
560
|
-
<a href="${hrefFor(page, locale.code === "vi" ? LOCALES.en : LOCALES.vi)}" hreflang="${locale.code === "vi" ? "en" : "vi"}">${locale.code === "vi" ? "English" : "Tiếng Việt"}</a>
|
|
561
|
-
<a href="${SITE.facebook}" target="_blank" rel="noopener noreferrer">Facebook profile</a>
|
|
562
|
-
</div>
|
|
563
|
-
</footer>
|
|
564
|
-
</div>
|
|
565
|
-
</main>
|
|
566
|
-
|
|
567
|
-
<aside class="toc" aria-label="${locale.ui.tocAria}">
|
|
568
|
-
<p class="toc-title">${locale.ui.tocTitle}</p>
|
|
569
|
-
<nav data-toc></nav>
|
|
570
|
-
</aside>
|
|
571
|
-
</div>
|
|
572
|
-
${ANALYTICS_SNIPPET} </body>
|
|
395
|
+
<body${home ? ' class="home"' : ""}>
|
|
396
|
+
${sprite}
|
|
397
|
+
<a class="skip-link" href="#content">${locale.ui.skip}</a>
|
|
398
|
+
${shell}${ANALYTICS_SNIPPET} </body>
|
|
573
399
|
</html>
|
|
574
400
|
`;
|
|
575
401
|
}
|
|
@@ -602,23 +428,54 @@ function assertPreBlocksIntact(slug, body, html) {
|
|
|
602
428
|
}
|
|
603
429
|
}
|
|
604
430
|
|
|
431
|
+
/** Page and heading entries the search palette loads on first use. */
|
|
432
|
+
function searchEntry(page, body, locale) {
|
|
433
|
+
const headings = [...body.matchAll(/<h([23])\b[^>]*\bid="([^"]+)"[^>]*>([\s\S]*?)<\/h\1>/g)]
|
|
434
|
+
.map(([, , id, text]) => ({ id, text: decodeEntities(stripTags(text)).replace(/\s+/g, " ").trim() }))
|
|
435
|
+
.filter((heading) => heading.text);
|
|
436
|
+
return {
|
|
437
|
+
href: hrefFor(page, locale),
|
|
438
|
+
icon: page.icon,
|
|
439
|
+
group: page.group,
|
|
440
|
+
nav: page.nav,
|
|
441
|
+
title: page.title,
|
|
442
|
+
lead: decodeEntities(stripTags(page.lead)),
|
|
443
|
+
headings
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
605
447
|
function build() {
|
|
606
|
-
const
|
|
448
|
+
const rootPackage = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
|
|
449
|
+
const version = rootPackage.version;
|
|
450
|
+
const piHost = rootPackage.peerDependencies["@earendil-works/pi-coding-agent"];
|
|
451
|
+
const releases = readReleases(path.join(siteRoot, "releases.json"));
|
|
607
452
|
/** @type {{file: string, html: string}[]} */
|
|
608
453
|
const outputs = [];
|
|
609
454
|
for (const locale of Object.values(LOCALES)) {
|
|
610
455
|
const pages = pagesFor(locale);
|
|
611
|
-
|
|
456
|
+
const index = [];
|
|
457
|
+
for (const [position, page] of pages.entries()) {
|
|
612
458
|
const fragment = path.join(locale.contentRoot, `${page.slug}.html`);
|
|
613
459
|
if (!fs.existsSync(fragment)) throw new Error(`missing content fragment: ${path.relative(repoRoot, fragment)}`);
|
|
614
|
-
|
|
460
|
+
let body = fs.readFileSync(fragment, "utf8").replace(/\s+$/, "");
|
|
615
461
|
if (body.includes("\t")) throw new Error(`${locale.code}/${page.slug}: content fragment contains a tab character`);
|
|
616
|
-
|
|
462
|
+
body = body
|
|
463
|
+
.replace("<!-- releases -->", () => renderReleaseNotes(releases, locale, { escapeHtml, icon }))
|
|
464
|
+
.replace("<!-- latest-releases -->", () => renderLatestReleases(releases, locale, { escapeHtml, icon, hrefFor }))
|
|
465
|
+
.replace("<!-- hero-pill -->", () => renderHeroPill(releases, locale, { escapeHtml, icon, hrefFor }))
|
|
466
|
+
.replaceAll("{{version}}", version)
|
|
467
|
+
.replaceAll("{{piHost}}", piHost)
|
|
468
|
+
.replaceAll("{{releaseCount}}", String(releases.filter((release) => release.status === "stable").length))
|
|
469
|
+
.replace(/\{\{icon:([a-z-]+)\}\}/g, (_, name) => icon(name));
|
|
470
|
+
const html = renderPage(page, position, body, version, locale, pages);
|
|
617
471
|
assertPreBlocksIntact(`${locale.code}/${page.slug}`, body, html);
|
|
618
472
|
const outputDir = locale.prefix ? path.join(siteRoot, locale.code) : siteRoot;
|
|
619
473
|
outputs.push({ file: path.join(outputDir, `${page.slug}.html`), html });
|
|
474
|
+
index.push(searchEntry(page, body, locale));
|
|
620
475
|
}
|
|
476
|
+
outputs.push({ file: path.join(siteRoot, "assets", `search-${locale.code}.json`), html: `${JSON.stringify(index)}\n` });
|
|
621
477
|
}
|
|
478
|
+
outputs.push({ file: path.join(repoRoot, "CHANGELOG.md"), html: renderChangelogMarkdown(releases) });
|
|
622
479
|
return outputs;
|
|
623
480
|
}
|
|
624
481
|
|
|
@@ -639,7 +496,7 @@ function main(argv) {
|
|
|
639
496
|
});
|
|
640
497
|
if (stale.length > 0) {
|
|
641
498
|
process.stderr.write(
|
|
642
|
-
`FAIL: docs-site output is stale for ${stale.map((item) => path.relative(
|
|
499
|
+
`FAIL: docs-site output is stale for ${stale.map((item) => path.relative(repoRoot, item.file)).join(", ")}. ` +
|
|
643
500
|
"Run: node scripts/build-docs-site.mjs\n"
|
|
644
501
|
);
|
|
645
502
|
return 1;
|