@trim21/personal-pi-extensions 0.0.367 → 0.0.372

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.367",
3
+ "version": "0.0.372",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
package/src/bwrap/core.ts CHANGED
@@ -252,6 +252,12 @@ export function createBwrapBashOperations(
252
252
  });
253
253
  if (signal?.aborted) throw new Error("aborted");
254
254
 
255
+ // 干净环境:不继承父进程 env/PATH,由 bash -lc 从 /etc/profile 与用户 profile 重建
256
+ const home = process.env.HOME;
257
+ if (home === undefined) {
258
+ throw new Error("HOME is not set; refusing to run bash in a clean environment");
259
+ }
260
+
255
261
  const seccompFd = resolved.network ? undefined : getSeccompFd();
256
262
  const baseArgs = [
257
263
  "--ro-bind",
@@ -266,8 +272,8 @@ export function createBwrapBashOperations(
266
272
  const child = spawn(
267
273
  findBwrap(resolved.bwrapPath),
268
274
  seccompFd === undefined
269
- ? [...baseArgs, "--", "bash", "-c", command]
270
- : [...baseArgs, "--seccomp", "3", "--", "bash", "-c", command],
275
+ ? [...baseArgs, "--", "bash", "-lc", command]
276
+ : [...baseArgs, "--seccomp", "3", "--", "bash", "-lc", command],
271
277
  {
272
278
  cwd,
273
279
  detached: true,
@@ -275,7 +281,12 @@ export function createBwrapBashOperations(
275
281
  seccompFd === undefined
276
282
  ? ["ignore", "pipe", "pipe"]
277
283
  : ["ignore", "pipe", "pipe", seccompFd],
278
- env: process.env,
284
+ env: {
285
+ HOME: home,
286
+ SHELL: "/bin/bash",
287
+ TERM: "dumb",
288
+ LANG: "C.UTF-8",
289
+ },
279
290
  },
280
291
  );
281
292
 
package/src/web/fetch.ts CHANGED
@@ -170,12 +170,28 @@ function classifyContentType(contentType: string): "html" | "text" | null {
170
170
  interface ParsedDocument {
171
171
  title: string | null;
172
172
  body: { textContent: string | null } | null;
173
+ querySelectorAll(selector: string): readonly {
174
+ id: string | null;
175
+ removeAttribute(name: string): void;
176
+ }[];
177
+ }
178
+
179
+ /**
180
+ * React 19 流式 SSR 把尚未 hydrate 的正文放在 <div hidden id="S:N"> 里暂存,
181
+ * 客户端接管后才移除 hidden。静态抓取时先解除,否则 readability 会把它当
182
+ * 隐藏内容丢弃,只留下 Suspense fallback(如 "Loading...")。
183
+ */
184
+ function unshadowReactStreaming(document: ParsedDocument): void {
185
+ for (const el of document.querySelectorAll("[hidden]")) {
186
+ if (/^S:\d+$/.test(el.id ?? "")) el.removeAttribute("hidden");
187
+ }
173
188
  }
174
189
 
175
190
  /** 从 HTML 提取标题 + 正文 markdown(readability 主内容 → turndown) */
176
191
  export function extractMarkdown(html: string, sourceUrl: string): FetchedPage {
177
192
  const parsed = parseHTML(html) as { document: ParsedDocument };
178
193
  const document = parsed.document;
194
+ unshadowReactStreaming(document);
179
195
  // tsconfig 无 DOM lib;Readability 构造参数声明为 DOM Document,运行时只用到
180
196
  // linkedom document 的兼容方法,cast 桥接即可
181
197
  const article = new Readability(parsed.document).parse();