@piagent/platform 1.1.8 → 1.2.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 +51 -1
- package/README.md +2 -2
- package/docs/command-reference-vietnamese.md +13 -4
- package/docs/distribution-standard.md +7 -7
- package/docs/mcp-and-tools.md +108 -2
- package/docs/model-options.md +10 -8
- package/docs/operator-manual-vietnamese.md +12 -12
- package/docs/publishing-for-teams.md +2 -0
- package/docs/quickstart-vietnamese.md +2 -2
- package/docs/release-install-policy.md +6 -6
- package/docs/security-threat-model.md +1 -1
- package/docs/team-onboarding.md +4 -4
- package/docs/vercel-docs-site.md +34 -4
- package/docs-site/architecture.html +152 -0
- package/docs-site/assets/docs.css +1001 -0
- package/docs-site/assets/docs.js +84 -0
- package/docs-site/commands.html +203 -0
- package/docs-site/content/architecture.html +29 -0
- package/docs-site/content/commands.html +80 -0
- package/docs-site/content/index.html +80 -0
- package/docs-site/content/mcp-approval.html +81 -0
- package/docs-site/content/mcp-auth.html +76 -0
- package/docs-site/content/mcp-servers.html +111 -0
- package/docs-site/content/mcp.html +120 -0
- package/docs-site/content/permissions.html +54 -0
- package/docs-site/content/profiles.html +68 -0
- package/docs-site/content/quickstart.html +144 -0
- package/docs-site/content/runtime.html +45 -0
- package/docs-site/content/scope.html +71 -0
- package/docs-site/content/security.html +51 -0
- package/docs-site/content/team.html +23 -0
- package/docs-site/content/workflows.html +47 -0
- package/docs-site/index.html +131 -1534
- package/docs-site/mcp-approval.html +204 -0
- package/docs-site/mcp-auth.html +199 -0
- package/docs-site/mcp-servers.html +234 -0
- package/docs-site/mcp.html +243 -0
- package/docs-site/permissions.html +177 -0
- package/docs-site/profiles.html +191 -0
- package/docs-site/quickstart.html +267 -0
- package/docs-site/runtime.html +168 -0
- package/docs-site/scope.html +194 -0
- package/docs-site/security.html +174 -0
- package/docs-site/team.html +142 -0
- package/docs-site/workflows.html +170 -0
- package/package.json +4 -2
- package/packages/piagent-core/capabilities/capability-core.js +17 -1
- package/packages/piagent-core/extensions/document-intake.ts +5 -5
- package/packages/piagent-core/extensions/piagent-guard.ts +319 -4
- package/packages/piagent-core/extensions/update-check.js +41 -0
- package/packages/piagent-core/mcp/mcp-approval-store.js +175 -0
- package/packages/piagent-core/mcp/mcp-auth-readiness.js +197 -0
- package/packages/piagent-core/mcp/mcp-command-actions.js +291 -0
- package/packages/piagent-core/mcp/mcp-config-layers.js +133 -0
- package/packages/piagent-core/mcp/mcp-server-catalog.js +150 -0
- package/packages/piagent-core/mcp/mcp-server-entry.js +242 -0
- package/packages/piagent-core/mcp/mcp-session-view.js +185 -0
- package/packages/piagent-core/package.json +7 -3
- package/packages/piagent-core/prompts/model-options.md +8 -8
- package/scripts/build-docs-site.mjs +421 -0
- package/scripts/configure-model-scope.sh +2 -2
- package/scripts/configure-subagents.sh +1 -1
- package/scripts/install-global.sh +5 -5
- package/scripts/mcp-manage.mjs +567 -0
- package/scripts/piagent-cli.mjs +1 -1
- package/scripts/preview-docs-site.mjs +88 -0
- package/scripts/setup.sh +2 -2
- package/scripts/team-doctor.sh +18 -2
- package/scripts/verify-local.sh +46 -19
- package/scripts/verify-release-identity.mjs +8 -2
- package/scripts/configure-mcp.sh +0 -349
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
// Assembles the docs site: one static page per topic, from a shared shell and a
|
|
7
|
+
// content fragment each.
|
|
8
|
+
//
|
|
9
|
+
// The site is served as plain files, so there is no runtime that could stitch a
|
|
10
|
+
// shared header into every page. Duplicating that markup by hand across a dozen
|
|
11
|
+
// pages is how a navigation entry ends up on eleven of them, so it is generated
|
|
12
|
+
// here instead and the output is committed. `--check` fails when the committed
|
|
13
|
+
// output no longer matches its source, the same arrangement the capability
|
|
14
|
+
// catalog already uses.
|
|
15
|
+
|
|
16
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
17
|
+
const siteRoot = path.join(repoRoot, "docs-site");
|
|
18
|
+
const contentRoot = path.join(siteRoot, "content");
|
|
19
|
+
|
|
20
|
+
const SITE = {
|
|
21
|
+
title: "Pi Agent Platform Docs",
|
|
22
|
+
origin: "https://piagent.io.vn",
|
|
23
|
+
repo: "https://github.com/Vt-mmm/piagent",
|
|
24
|
+
facebook: "https://www.facebook.com/vinhtam0544/"
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Order here is the order in the sidebar, and it is also the order of the
|
|
28
|
+
// previous/next links at the foot of each page.
|
|
29
|
+
const NAV = [
|
|
30
|
+
{
|
|
31
|
+
group: "Get started",
|
|
32
|
+
pages: [
|
|
33
|
+
{
|
|
34
|
+
slug: "index",
|
|
35
|
+
href: "/",
|
|
36
|
+
nav: "Overview",
|
|
37
|
+
title: "Pi Agent Platform",
|
|
38
|
+
lead: "Nền tảng dùng lại cho nhiều dự án: cài đặt, profile, quyền chạy, MCP, bảo mật và workflow trong một tài liệu."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
slug: "quickstart",
|
|
42
|
+
nav: "Quickstart",
|
|
43
|
+
title: "Step by step cho thành viên mới",
|
|
44
|
+
lead: "Hai lệnh để cài, rồi mở repo, chọn profile, bật quyền phù hợp, chạy task, và verify bằng bằng chứng thật trước khi handoff."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
slug: "scope",
|
|
48
|
+
nav: "Phạm vi platform",
|
|
49
|
+
title: "Platform này nhận phần nào về mình",
|
|
50
|
+
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."
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
group: "Build",
|
|
56
|
+
pages: [
|
|
57
|
+
{
|
|
58
|
+
slug: "profiles",
|
|
59
|
+
nav: "Project profile",
|
|
60
|
+
title: "Chọn đúng đường ray cho repo",
|
|
61
|
+
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."
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
slug: "permissions",
|
|
65
|
+
nav: "Permission modes",
|
|
66
|
+
title: "Một command để đổi quyền theo task",
|
|
67
|
+
lead: "Permission mode là session-local override. Đổi mode không sửa project profile."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
slug: "commands",
|
|
71
|
+
nav: "Command catalog",
|
|
72
|
+
title: "Command ngắn, rõ, ít token burn",
|
|
73
|
+
lead: "Ưu tiên dùng slash command vì mỗi command đã gói sẵn policy và flow. Thành viên không cần nhớ lại quy trình trong prompt."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
slug: "workflows",
|
|
77
|
+
nav: "Workflows",
|
|
78
|
+
title: "Các luồng chính",
|
|
79
|
+
lead: "Chọn workflow theo intent thay vì mô tả lại policy trong prompt. Cách này giảm context lãng phí và giữ hành vi nhất quán."
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
group: "MCP",
|
|
85
|
+
pages: [
|
|
86
|
+
{
|
|
87
|
+
slug: "mcp",
|
|
88
|
+
nav: "MCP overview",
|
|
89
|
+
title: "MCP trong Pi Agent Platform",
|
|
90
|
+
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."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
slug: "mcp-servers",
|
|
94
|
+
nav: "Quản lý server",
|
|
95
|
+
title: "Thêm, xem và gỡ MCP server",
|
|
96
|
+
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."
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
slug: "mcp-auth",
|
|
100
|
+
nav: "Auth và readiness",
|
|
101
|
+
title: "Đăng nhập và trạng thái sẵn sàng",
|
|
102
|
+
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."
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
slug: "mcp-approval",
|
|
106
|
+
nav: "Approval gate",
|
|
107
|
+
title: "Server đến từ repo phải được duyệt",
|
|
108
|
+
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."
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
group: "Operate",
|
|
114
|
+
pages: [
|
|
115
|
+
{
|
|
116
|
+
slug: "security",
|
|
117
|
+
nav: "Security model",
|
|
118
|
+
title: "Policy enforcement layer, không phải OS sandbox",
|
|
119
|
+
lead: "Platform ưu tiên fail-closed trong bề mặt tool được kiểm soát: khi lock sai, profile sai hoặc bằng chứng thiếu thì chặn, không đoán."
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
slug: "architecture",
|
|
123
|
+
nav: "Architecture",
|
|
124
|
+
title: "Kiến trúc repo",
|
|
125
|
+
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."
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
slug: "runtime",
|
|
129
|
+
nav: "Runtime tools",
|
|
130
|
+
title: "Terminal commands cho operator",
|
|
131
|
+
lead: "Nhóm command này chạy ngoài slash command, dùng cho setup, doctor, benchmark, capability và MCP."
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
slug: "team",
|
|
135
|
+
nav: "Team usage",
|
|
136
|
+
title: "Team nên dùng như một chuẩn vận hành",
|
|
137
|
+
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 đó."
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
|
|
143
|
+
const pages = NAV.flatMap((section) => section.pages.map((page) => ({ ...page, group: section.group })));
|
|
144
|
+
|
|
145
|
+
/** @param {{slug: string, href?: string}} page @returns {string} */
|
|
146
|
+
function hrefFor(page) {
|
|
147
|
+
return page.href ?? `/${page.slug}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @param {string} value @returns {string} */
|
|
151
|
+
function escapeHtml(value) {
|
|
152
|
+
return value
|
|
153
|
+
.replace(/&/g, "&")
|
|
154
|
+
.replace(/</g, "<")
|
|
155
|
+
.replace(/>/g, ">")
|
|
156
|
+
.replace(/"/g, """);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A lead written with inline markup, as plain text for the meta description.
|
|
161
|
+
*
|
|
162
|
+
* Stripping once is not enough: removing `<b>` from `<<b>script>` leaves
|
|
163
|
+
* `<script>`, so a single pass can produce a tag it was meant to remove. The
|
|
164
|
+
* loop runs until the text stops changing. Safety does not rest on this —
|
|
165
|
+
* `escapeHtml` still runs on the result — but a meta description containing
|
|
166
|
+
* stray markup is wrong regardless of whether it is dangerous.
|
|
167
|
+
*
|
|
168
|
+
* @param {string} value
|
|
169
|
+
* @returns {string}
|
|
170
|
+
*/
|
|
171
|
+
function stripTags(value) {
|
|
172
|
+
let text = value;
|
|
173
|
+
let previous;
|
|
174
|
+
do {
|
|
175
|
+
previous = text;
|
|
176
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
177
|
+
} while (text !== previous);
|
|
178
|
+
return text;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** @param {string} currentSlug @returns {string} */
|
|
182
|
+
function renderNav(currentSlug) {
|
|
183
|
+
return NAV.map((section) => {
|
|
184
|
+
const links = section.pages
|
|
185
|
+
.map((page) => {
|
|
186
|
+
const current = page.slug === currentSlug;
|
|
187
|
+
const classes = current ? "nav-link current" : "nav-link";
|
|
188
|
+
const aria = current ? ' aria-current="page"' : "";
|
|
189
|
+
return ` <a class="${classes}" href="${hrefFor(page)}"${aria}>${escapeHtml(page.nav)}</a>`;
|
|
190
|
+
})
|
|
191
|
+
.join("\n");
|
|
192
|
+
return ` <div class="nav-group">\n <p class="nav-title">${escapeHtml(section.group)}</p>\n${links}\n </div>`;
|
|
193
|
+
}).join("\n");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** @param {typeof pages[number]} page @returns {string} */
|
|
197
|
+
function renderBreadcrumb(page) {
|
|
198
|
+
if (page.slug === "index") return "";
|
|
199
|
+
return [
|
|
200
|
+
' <nav class="breadcrumb" aria-label="Breadcrumb">',
|
|
201
|
+
' <a href="/">Docs</a>',
|
|
202
|
+
' <span aria-hidden="true">/</span>',
|
|
203
|
+
` <span>${escapeHtml(page.group)}</span>`,
|
|
204
|
+
' <span aria-hidden="true">/</span>',
|
|
205
|
+
` <span>${escapeHtml(page.nav)}</span>`,
|
|
206
|
+
" </nav>",
|
|
207
|
+
""
|
|
208
|
+
].join("\n");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** @param {typeof pages[number]} page @returns {string} */
|
|
212
|
+
function renderPageHead(page) {
|
|
213
|
+
// The landing page carries its own hero, so it is not given a generated head.
|
|
214
|
+
if (page.slug === "index") return "";
|
|
215
|
+
return [
|
|
216
|
+
' <div class="page-head">',
|
|
217
|
+
` <h1>${escapeHtml(page.title)}</h1>`,
|
|
218
|
+
` <p>${page.lead}</p>`,
|
|
219
|
+
" </div>",
|
|
220
|
+
""
|
|
221
|
+
].join("\n");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** @param {number} index @returns {string} */
|
|
225
|
+
function renderPager(index) {
|
|
226
|
+
const previous = pages[index - 1];
|
|
227
|
+
const next = pages[index + 1];
|
|
228
|
+
if (!previous && !next) return "";
|
|
229
|
+
const parts = [' <nav class="pager" aria-label="Trang kế tiếp">'];
|
|
230
|
+
if (previous) {
|
|
231
|
+
parts.push(
|
|
232
|
+
` <a class="pager-prev" href="${hrefFor(previous)}">`,
|
|
233
|
+
' <span class="pager-label">Trước</span>',
|
|
234
|
+
` <span class="pager-title">${escapeHtml(previous.nav)}</span>`,
|
|
235
|
+
" </a>"
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (next) {
|
|
239
|
+
parts.push(
|
|
240
|
+
` <a class="pager-next" href="${hrefFor(next)}">`,
|
|
241
|
+
' <span class="pager-label">Tiếp</span>',
|
|
242
|
+
` <span class="pager-title">${escapeHtml(next.nav)}</span>`,
|
|
243
|
+
" </a>"
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
parts.push(" </nav>", "");
|
|
247
|
+
return parts.join("\n");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* @param {typeof pages[number]} page
|
|
252
|
+
* @param {number} index
|
|
253
|
+
* @param {string} body
|
|
254
|
+
* @param {string} version
|
|
255
|
+
* @returns {string}
|
|
256
|
+
*/
|
|
257
|
+
function renderPage(page, index, body, version) {
|
|
258
|
+
const canonical = page.slug === "index" ? `${SITE.origin}/` : `${SITE.origin}${hrefFor(page)}`;
|
|
259
|
+
const documentTitle = page.slug === "index" ? SITE.title : `${page.title} · ${SITE.title}`;
|
|
260
|
+
const description = stripTags(page.lead);
|
|
261
|
+
|
|
262
|
+
return `<!doctype html>
|
|
263
|
+
<html lang="vi">
|
|
264
|
+
<head>
|
|
265
|
+
<meta charset="utf-8" />
|
|
266
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
267
|
+
<title>${escapeHtml(documentTitle)}</title>
|
|
268
|
+
<meta name="description" content="${escapeHtml(description)}" />
|
|
269
|
+
<meta name="theme-color" content="#050505" />
|
|
270
|
+
<link rel="canonical" href="${canonical}" />
|
|
271
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
272
|
+
<meta property="og:title" content="${escapeHtml(documentTitle)}" />
|
|
273
|
+
<meta property="og:description" content="${escapeHtml(description)}" />
|
|
274
|
+
<meta property="og:url" content="${canonical}" />
|
|
275
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
276
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
277
|
+
<link
|
|
278
|
+
href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap"
|
|
279
|
+
rel="stylesheet"
|
|
280
|
+
/>
|
|
281
|
+
<link rel="stylesheet" href="/assets/docs.css" />
|
|
282
|
+
<script src="/assets/docs.js" defer></script>
|
|
283
|
+
</head>
|
|
284
|
+
<body>
|
|
285
|
+
<header class="topbar">
|
|
286
|
+
<a class="brand" href="/" aria-label="Pi Agent Platform">
|
|
287
|
+
<span class="brand-mark"><img src="/assets/piagent-logo.svg" alt="" /></span>
|
|
288
|
+
<span>Pi Agent Platform</span>
|
|
289
|
+
</a>
|
|
290
|
+
<div class="top-actions">
|
|
291
|
+
<span class="pill"><span class="pill-dot"></span>v${version} docs</span>
|
|
292
|
+
<a class="button social" href="${SITE.repo}" target="_blank" rel="noopener noreferrer" aria-label="Mở GitHub repository">
|
|
293
|
+
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
294
|
+
<path d="M7 17 17 7M9 7h8v8" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
295
|
+
</svg>
|
|
296
|
+
GitHub
|
|
297
|
+
</a>
|
|
298
|
+
<button class="button mobile-menu" type="button" data-menu>Lộ trình</button>
|
|
299
|
+
<a class="button" href="/commands">Commands</a>
|
|
300
|
+
</div>
|
|
301
|
+
</header>
|
|
302
|
+
|
|
303
|
+
<div class="layout with-toc">
|
|
304
|
+
<aside class="sidebar" data-sidebar>
|
|
305
|
+
<div class="sidebar-search" aria-label="Tìm trong tài liệu">
|
|
306
|
+
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
307
|
+
<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" />
|
|
308
|
+
</svg>
|
|
309
|
+
<input type="search" placeholder="Tìm trang" data-filter />
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<nav aria-label="Tài liệu">
|
|
313
|
+
${renderNav(page.slug)}
|
|
314
|
+
</nav>
|
|
315
|
+
</aside>
|
|
316
|
+
|
|
317
|
+
<main>
|
|
318
|
+
<div class="content-shell" data-article>
|
|
319
|
+
${renderBreadcrumb(page)}${renderPageHead(page)}${body}
|
|
320
|
+
${renderPager(index)}
|
|
321
|
+
<footer class="footer">
|
|
322
|
+
<p>Pi Agent Platform docs · static HTML · governed agent workflow</p>
|
|
323
|
+
<div class="footer-links" aria-label="External links">
|
|
324
|
+
<a href="${SITE.origin}" target="_blank" rel="noopener noreferrer">piagent.io.vn</a>
|
|
325
|
+
<a href="${SITE.repo}" target="_blank" rel="noopener noreferrer">GitHub repository</a>
|
|
326
|
+
<a href="${SITE.facebook}" target="_blank" rel="noopener noreferrer">Facebook profile</a>
|
|
327
|
+
</div>
|
|
328
|
+
</footer>
|
|
329
|
+
</div>
|
|
330
|
+
</main>
|
|
331
|
+
|
|
332
|
+
<aside class="toc" aria-label="Mục lục trang">
|
|
333
|
+
<p class="toc-title">Trên trang này</p>
|
|
334
|
+
<nav data-toc></nav>
|
|
335
|
+
</aside>
|
|
336
|
+
</div>
|
|
337
|
+
</body>
|
|
338
|
+
</html>
|
|
339
|
+
`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Every `<pre>` block in a document, with its inner text unmodified. */
|
|
343
|
+
function preBlocks(html) {
|
|
344
|
+
return [...html.matchAll(/<pre\b[^>]*>([\s\S]*?)<\/pre>/g)].map((match) => match[1]);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* The body is placed into the shell verbatim. Indenting it to line up with the
|
|
349
|
+
* surrounding markup would look tidier in the source and would also rewrite the
|
|
350
|
+
* inside of every `<pre>`, where whitespace is what the reader sees. This
|
|
351
|
+
* refuses to emit a page whose code blocks no longer match the fragment.
|
|
352
|
+
*
|
|
353
|
+
* @param {string} slug
|
|
354
|
+
* @param {string} body
|
|
355
|
+
* @param {string} html
|
|
356
|
+
*/
|
|
357
|
+
function assertPreBlocksIntact(slug, body, html) {
|
|
358
|
+
const source = preBlocks(body);
|
|
359
|
+
const rendered = preBlocks(html);
|
|
360
|
+
if (source.length !== rendered.length) {
|
|
361
|
+
throw new Error(`${slug}: expected ${source.length} code blocks in the output, found ${rendered.length}`);
|
|
362
|
+
}
|
|
363
|
+
for (const [index, expected] of source.entries()) {
|
|
364
|
+
if (rendered[index] !== expected) {
|
|
365
|
+
throw new Error(`${slug}: code block ${index + 1} was altered between the fragment and the page`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function build() {
|
|
371
|
+
const version = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")).version;
|
|
372
|
+
/** @type {{file: string, html: string}[]} */
|
|
373
|
+
const outputs = [];
|
|
374
|
+
for (const [index, page] of pages.entries()) {
|
|
375
|
+
const fragment = path.join(contentRoot, `${page.slug}.html`);
|
|
376
|
+
if (!fs.existsSync(fragment)) throw new Error(`missing content fragment: ${path.relative(repoRoot, fragment)}`);
|
|
377
|
+
const body = fs.readFileSync(fragment, "utf8").replace(/\s+$/, "");
|
|
378
|
+
if (body.includes("\t")) throw new Error(`${page.slug}: content fragment contains a tab character`);
|
|
379
|
+
const html = renderPage(page, index, body, version);
|
|
380
|
+
assertPreBlocksIntact(page.slug, body, html);
|
|
381
|
+
outputs.push({ file: path.join(siteRoot, `${page.slug}.html`), html });
|
|
382
|
+
}
|
|
383
|
+
return outputs;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function main(argv) {
|
|
387
|
+
const check = argv.includes("--check");
|
|
388
|
+
let outputs;
|
|
389
|
+
try {
|
|
390
|
+
outputs = build();
|
|
391
|
+
} catch (error) {
|
|
392
|
+
process.stderr.write(`FAIL: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
393
|
+
return 1;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (check) {
|
|
397
|
+
const stale = outputs.filter((output) => {
|
|
398
|
+
const current = fs.existsSync(output.file) ? fs.readFileSync(output.file, "utf8") : "";
|
|
399
|
+
return current !== output.html;
|
|
400
|
+
});
|
|
401
|
+
if (stale.length > 0) {
|
|
402
|
+
process.stderr.write(
|
|
403
|
+
`FAIL: docs-site output is stale for ${stale.map((item) => path.basename(item.file)).join(", ")}. ` +
|
|
404
|
+
"Run: node scripts/build-docs-site.mjs\n"
|
|
405
|
+
);
|
|
406
|
+
return 1;
|
|
407
|
+
}
|
|
408
|
+
process.stdout.write(`${JSON.stringify({ ok: true, pages: outputs.length })}\n`);
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
for (const output of outputs) fs.writeFileSync(output.file, output.html);
|
|
413
|
+
process.stdout.write(`${JSON.stringify({ ok: true, pages: outputs.length, out: path.relative(repoRoot, siteRoot) })}\n`);
|
|
414
|
+
return 0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
418
|
+
process.exit(main(process.argv.slice(2)));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export { NAV, build, main };
|
|
@@ -7,7 +7,7 @@ Usage:
|
|
|
7
7
|
scripts/configure-model-scope.sh [options]
|
|
8
8
|
|
|
9
9
|
Options:
|
|
10
|
-
--preset <full|codex|claude> Model
|
|
10
|
+
--preset <full|codex|claude> Model families to enable (default: full = both)
|
|
11
11
|
--default-model <provider/model[:thinking]>
|
|
12
12
|
Default model (default: openai-codex/gpt-5.5:xhigh)
|
|
13
13
|
--settings <path> Pi settings file (default: ~/.pi/agent/settings.json)
|
|
@@ -67,7 +67,7 @@ done
|
|
|
67
67
|
case "$PRESET" in
|
|
68
68
|
full|codex|claude) ;;
|
|
69
69
|
*)
|
|
70
|
-
echo "FAIL: --preset must be full, codex, or claude" >&2
|
|
70
|
+
echo "FAIL: --preset must be full, codex, or claude (model families)" >&2
|
|
71
71
|
exit 2
|
|
72
72
|
;;
|
|
73
73
|
esac
|
|
@@ -14,7 +14,7 @@ Options:
|
|
|
14
14
|
Runtime preset (default: safe)
|
|
15
15
|
--config <path> Subagent config path (default: ~/.pi/agent/extensions/subagent/config.json)
|
|
16
16
|
--settings <path> Pi settings path (default: ~/.pi/agent/settings.json)
|
|
17
|
-
--model-scope <none|piagent|codex|claude>
|
|
17
|
+
--model-scope <none|piagent|codex|claude> model family for subagents
|
|
18
18
|
Optional subagent model allowlist written to settings (default: none)
|
|
19
19
|
--dry-run Print merged config/settings summary without writing
|
|
20
20
|
--list Print available presets
|
|
@@ -15,7 +15,7 @@ Package source examples:
|
|
|
15
15
|
# Installs the stable tag after resolving it to a commit SHA.
|
|
16
16
|
|
|
17
17
|
# Pin a specific release:
|
|
18
|
-
scripts/install-global.sh --version v1.
|
|
18
|
+
scripts/install-global.sh --version v1.2.0 --resolve-tag
|
|
19
19
|
|
|
20
20
|
# Preview the planned install/update without changing user config:
|
|
21
21
|
scripts/install-global.sh --stable --dry-run
|
|
@@ -127,7 +127,7 @@ detect_runtime_surface() {
|
|
|
127
127
|
printf 'native-windows/%s (not release-gated; terminal helpers require Bash semantics)' "$arch_name"
|
|
128
128
|
;;
|
|
129
129
|
*)
|
|
130
|
-
printf '%s/%s (outside v1.
|
|
130
|
+
printf '%s/%s (outside v1.2.0 release matrix)' "$os_name" "$arch_name"
|
|
131
131
|
;;
|
|
132
132
|
esac
|
|
133
133
|
}
|
|
@@ -135,7 +135,7 @@ detect_runtime_surface() {
|
|
|
135
135
|
warn_runtime_surface_if_needed() {
|
|
136
136
|
local surface="$1"
|
|
137
137
|
case "$surface" in
|
|
138
|
-
*"supported target"*|*"experimental"*|*"not release-gated"*|*"outside v1.
|
|
138
|
+
*"supported target"*|*"experimental"*|*"not release-gated"*|*"outside v1.2.0 release matrix"*)
|
|
139
139
|
echo "WARN: runtime surface is $surface." >&2
|
|
140
140
|
echo "WARN: For team rollout, run piagent-doctor plus the project smoke/verify suite on this machine before relying on it." >&2
|
|
141
141
|
;;
|
|
@@ -354,7 +354,7 @@ require_node_version
|
|
|
354
354
|
# Node check, not before: the validator is a Node script, so on a machine without
|
|
355
355
|
# Node it reported a missing interpreter as a bad preset.
|
|
356
356
|
if [[ "$WITH_MCP" == true ]]; then
|
|
357
|
-
if !
|
|
357
|
+
if ! node "$PLATFORM_ROOT/scripts/mcp-manage.mjs" --scope global --preset "$MCP_PRESET" --validate-preset >/dev/null; then
|
|
358
358
|
echo "Nothing was installed." >&2
|
|
359
359
|
exit 2
|
|
360
360
|
fi
|
|
@@ -508,7 +508,7 @@ if [[ "$WITH_MCP" == true ]]; then
|
|
|
508
508
|
pi-mcp-adapter init || true
|
|
509
509
|
fi
|
|
510
510
|
echo "Configuring shared global MCP baseline:"
|
|
511
|
-
run_cmd
|
|
511
|
+
run_cmd node "$PLATFORM_ROOT/scripts/mcp-manage.mjs" --scope global --preset "$MCP_PRESET" --replace
|
|
512
512
|
fi
|
|
513
513
|
|
|
514
514
|
if [[ "$WITH_SUBAGENTS" == true ]]; then
|