pi-extensions-tool-display 0.1.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/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # pi-extensions-tool-display
2
+
3
+ `pi-extensions-tool-display` provides the shared tool-result rendering protocol and component helpers used by Pi extensions.
4
+
5
+ It provides:
6
+
7
+ - registration, disposal, and activity checks for result-render middleware;
8
+ - a pending registration queue for when the Pi display host loads later;
9
+ - safe component detection and a helper for appending an audit panel to the original tool result.
10
+
11
+ It is not a standalone Pi extension: it registers no tools, commands, or entry renderers. It is intended to be consumed as a runtime dependency by extensions such as `pi-distill` and `pi-tool-supervisor`.
12
+
13
+ ## Boundary
14
+
15
+ This package owns the display protocol and generic component composition only. Each feature extension continues to own its domain-specific audit extraction, copy, status, and layout.
16
+
17
+ ## Upstream attribution
18
+
19
+ The display integration is designed to work with the MIT-licensed [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display), the original full-featured Pi tool-display project. We thank MasuRii for that work.
20
+
21
+ This package is a separate shared-runtime extraction from this repository's own extension bridges. It does not bundle or redistribute `pi-tool-display`'s implementation; the upstream project remains the reference for full tool rendering.
22
+
23
+ ## Development
24
+
25
+ ```bash
26
+ npm test
27
+ npm run typecheck
28
+ ```
29
+
30
+ ## License
31
+
32
+ [MIT](../../LICENSE)
@@ -0,0 +1,32 @@
1
+ # pi-extensions-tool-display
2
+
3
+ `pi-extensions-tool-display` 是 Pi 扩展共享的工具结果展示协议和组件工具包。
4
+
5
+ 它提供:
6
+
7
+ - 结果渲染 middleware 的注册、注销和状态判断;
8
+ - Pi 展示宿主尚未加载时的 pending 注册队列;
9
+ - 安全识别组件,以及把审计面板追加到原始工具结果后的通用组件组合。
10
+
11
+ 它不是一个独立的 Pi 扩展,不注册工具、命令或 entry renderer。通常由 `pi-distill`、`pi-tool-supervisor` 等扩展作为运行时依赖使用。
12
+
13
+ ## 设计边界
14
+
15
+ 这个包只负责展示协议和通用组件组合。具体业务的审计数据提取、文案、状态和布局仍由调用方维护,避免把不同扩展耦合在一起。
16
+
17
+ ## 上游引用
18
+
19
+ 本包的展示集成设计用于兼容 MIT 许可的 [`MasuRii/pi-tool-display`](https://github.com/MasuRii/pi-tool-display),这是 Pi 生态中原始的完整工具展示项目。感谢 MasuRii 的工作。
20
+
21
+ 本包是从本仓库自身的扩展 bridge 中提取出的独立共享运行时,不打包或再发布 `pi-tool-display` 的实现;完整工具渲染仍以上游项目为参考实现。
22
+
23
+ ## 开发
24
+
25
+ ```bash
26
+ npm test
27
+ npm run typecheck
28
+ ```
29
+
30
+ ## 许可证
31
+
32
+ [MIT](../../LICENSE)
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "pi-extensions-tool-display",
3
+ "version": "0.1.1",
4
+ "description": "Shared result-rendering protocol and component helpers for Pi extensions",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "README.md",
13
+ "README.zh-CN.md",
14
+ "tsconfig.json"
15
+ ],
16
+ "scripts": {
17
+ "test": "tsx --test tests/tool-display.test.ts",
18
+ "typecheck": "tsc --noEmit --pretty false",
19
+ "build": "npm run typecheck",
20
+ "check": "npm run typecheck && npm test && npm pack --dry-run --json > /dev/null"
21
+ },
22
+ "engines": {
23
+ "node": ">=22"
24
+ },
25
+ "license": "MIT",
26
+ "author": "maplezzk",
27
+ "homepage": "https://github.com/maplezzk/pi-extensions/tree/main/packages/pi-extensions-tool-display",
28
+ "bugs": "https://github.com/maplezzk/pi-extensions/issues",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/maplezzk/pi-extensions.git",
32
+ "directory": "packages/pi-extensions-tool-display"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "registry": "https://registry.npmjs.org"
37
+ },
38
+ "keywords": [
39
+ "pi-package",
40
+ "pi",
41
+ "pi-extension",
42
+ "coding-agent",
43
+ "tool-display"
44
+ ],
45
+ "peerDependencies": {
46
+ "@earendil-works/pi-tui": ">=0.80.0 <0.81.0"
47
+ },
48
+ "devDependencies": {
49
+ "@earendil-works/pi-tui": "0.80.10",
50
+ "@types/node": "24.12.4",
51
+ "tsx": "4.23.1",
52
+ "typescript": "5.9.3"
53
+ }
54
+ }
package/src/index.ts ADDED
@@ -0,0 +1,98 @@
1
+ import { Container, type Component } from "@earendil-works/pi-tui";
2
+
3
+ export const TOOL_DISPLAY_API_KEY = Symbol.for("pi-tool-display.api.v1");
4
+ export const PENDING_MIDDLEWARES_KEY = Symbol.for("pi-tool-display.pendingResultRenderMiddlewares.v1");
5
+
6
+ export type RenderTheme = {
7
+ fg(color: string, text: string): string;
8
+ bold(text: string): string;
9
+ };
10
+
11
+ export type ResultMiddlewareContext = {
12
+ toolName: string;
13
+ result: unknown;
14
+ options: { expanded?: boolean };
15
+ theme: RenderTheme;
16
+ };
17
+
18
+ export type ResultMiddleware = (
19
+ context: ResultMiddlewareContext,
20
+ next: () => unknown,
21
+ ) => unknown;
22
+
23
+ export type MiddlewareRegistration = {
24
+ id: string;
25
+ toolName: string;
26
+ middleware: ResultMiddleware;
27
+ };
28
+
29
+ export type ToolDisplayApi = {
30
+ registerResultRenderMiddleware?(registration: MiddlewareRegistration): string;
31
+ unregisterResultRenderMiddleware?(id: string): boolean;
32
+ hasResultRenderMiddleware?(id: string): boolean;
33
+ isResultRenderPipelineActive?(toolName: string): boolean;
34
+ };
35
+
36
+ type GlobalProtocol = typeof globalThis & {
37
+ [TOOL_DISPLAY_API_KEY]?: ToolDisplayApi;
38
+ [PENDING_MIDDLEWARES_KEY]?: MiddlewareRegistration[];
39
+ };
40
+
41
+ function getApi(): ToolDisplayApi | undefined {
42
+ return (globalThis as GlobalProtocol)[TOOL_DISPLAY_API_KEY];
43
+ }
44
+
45
+ function queueRegistration(registration: MiddlewareRegistration): void {
46
+ const globalProtocol = globalThis as GlobalProtocol;
47
+ const queue = Array.isArray(globalProtocol[PENDING_MIDDLEWARES_KEY])
48
+ ? globalProtocol[PENDING_MIDDLEWARES_KEY]!
49
+ : [];
50
+ const index = queue.findIndex((entry) => entry?.id === registration.id);
51
+ if (index >= 0) queue[index] = registration;
52
+ else queue.push(registration);
53
+ globalProtocol[PENDING_MIDDLEWARES_KEY] = queue;
54
+ }
55
+
56
+ function removeQueuedRegistration(id: string): void {
57
+ const queue = (globalThis as GlobalProtocol)[PENDING_MIDDLEWARES_KEY];
58
+ if (!Array.isArray(queue)) return;
59
+ const index = queue.findIndex((entry) => entry?.id === id);
60
+ if (index >= 0) queue.splice(index, 1);
61
+ }
62
+
63
+ export function registerResultRenderMiddleware(
64
+ registration: MiddlewareRegistration,
65
+ ): () => void {
66
+ const api = getApi();
67
+ if (typeof api?.registerResultRenderMiddleware === "function") {
68
+ api.registerResultRenderMiddleware(registration);
69
+ } else {
70
+ queueRegistration(registration);
71
+ }
72
+
73
+ return () => {
74
+ getApi()?.unregisterResultRenderMiddleware?.(registration.id);
75
+ removeQueuedRegistration(registration.id);
76
+ };
77
+ }
78
+
79
+ export function isResultRenderMiddlewareActive(id: string, toolName: string): boolean {
80
+ const api = getApi();
81
+ return api?.hasResultRenderMiddleware?.(id) === true
82
+ && api.isResultRenderPipelineActive?.(toolName) === true;
83
+ }
84
+
85
+ export function asComponent(value: unknown): Component | undefined {
86
+ return value && typeof value === "object" && typeof (value as Component).render === "function"
87
+ ? value as Component
88
+ : undefined;
89
+ }
90
+
91
+ export function appendResultRenderPanel(baseValue: unknown, panel: Component): Component {
92
+ const base = asComponent(baseValue);
93
+ if (!base) return panel;
94
+ const container = new Container();
95
+ container.addChild(base);
96
+ container.addChild(panel);
97
+ return container;
98
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "allowImportingTsExtensions": true,
7
+ "noEmit": true,
8
+ "skipLibCheck": true,
9
+ "strict": false,
10
+ "types": ["node"]
11
+ },
12
+ "include": ["src/**/*.ts", "tests/**/*.ts"]
13
+ }