@buyi1net/pi-toolkit 0.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/LICENSE +21 -0
- package/README.md +45 -0
- package/assembler.ts +127 -0
- package/config.ts +201 -0
- package/i18n.ts +693 -0
- package/index.ts +99 -0
- package/menu/items.ts +264 -0
- package/menu/panels.ts +91 -0
- package/menu/settings-list.ts +84 -0
- package/menu/theme.ts +31 -0
- package/menu/toolkit-menu.ts +139 -0
- package/module.ts +156 -0
- package/modules/eyes/chain.ts +173 -0
- package/modules/eyes/config.ts +319 -0
- package/modules/eyes/index.ts +161 -0
- package/modules/eyes/menu.ts +828 -0
- package/modules/eyes/pi-model-backend.ts +245 -0
- package/modules/eyes/resilience.ts +161 -0
- package/modules/eyes/vision-bridge.ts +232 -0
- package/modules/eyes/vision-cache.ts +86 -0
- package/modules/eyes/vision-json.ts +19 -0
- package/modules/eyes/vision-preprocess.ts +192 -0
- package/modules/eyes/vision-probe.ts +21 -0
- package/modules/eyes/vision-prompt.ts +90 -0
- package/modules/eyes/vision-tool.ts +59 -0
- package/modules/eyes/vision-types.ts +33 -0
- package/modules/index.ts +10 -0
- package/modules/subagents/agents/researcher.md +51 -0
- package/modules/subagents/agents/scout.md +40 -0
- package/modules/subagents/agents/worker.md +79 -0
- package/modules/subagents/config.json.example +6 -0
- package/modules/subagents/config.ts +144 -0
- package/modules/subagents/index.ts +91 -0
- package/modules/subagents/menu.ts +388 -0
- package/modules/subagents/src/activity.ts +511 -0
- package/modules/subagents/src/agents.ts +126 -0
- package/modules/subagents/src/command.ts +37 -0
- package/modules/subagents/src/dependencies.ts +246 -0
- package/modules/subagents/src/diagnostics.ts +13 -0
- package/modules/subagents/src/display.ts +94 -0
- package/modules/subagents/src/headless.ts +342 -0
- package/modules/subagents/src/herdr.ts +203 -0
- package/modules/subagents/src/index.ts +2798 -0
- package/modules/subagents/src/inspect-tool.ts +839 -0
- package/modules/subagents/src/launch-config.ts +196 -0
- package/modules/subagents/src/layout-budget.ts +26 -0
- package/modules/subagents/src/list-tool.ts +292 -0
- package/modules/subagents/src/message-tool.ts +808 -0
- package/modules/subagents/src/names.ts +17 -0
- package/modules/subagents/src/pane-layout.ts +49 -0
- package/modules/subagents/src/params.ts +145 -0
- package/modules/subagents/src/renderers.ts +181 -0
- package/modules/subagents/src/result.ts +43 -0
- package/modules/subagents/src/retention.ts +114 -0
- package/modules/subagents/src/route-error.ts +212 -0
- package/modules/subagents/src/routing.ts +235 -0
- package/modules/subagents/src/runtime-registry.ts +159 -0
- package/modules/subagents/src/session.ts +801 -0
- package/modules/subagents/src/status.ts +513 -0
- package/modules/subagents/src/stop-tool.ts +235 -0
- package/modules/subagents/src/subagent-done.ts +590 -0
- package/modules/subagents/src/subagent-tool.ts +1155 -0
- package/modules/subagents/src/surface.ts +355 -0
- package/modules/subagents/src/team-dispatch-tool.ts +219 -0
- package/modules/subagents/src/team.ts +232 -0
- package/modules/subagents/src/tmux.ts +210 -0
- package/modules/subagents/src/tools/safe-bash.ts +72 -0
- package/modules/subagents/src/types.ts +131 -0
- package/modules/tui/adapter/provider-usage.ts +143 -0
- package/modules/tui/config.ts +50 -0
- package/modules/tui/index.ts +120 -0
- package/modules/tui/kernel/pkg/shared/grok-subscription.ts +169 -0
- package/modules/tui/kernel/pkg/shared/official-subscription.ts +237 -0
- package/modules/tui/kernel/pkg/shared/provider-catalog.ts +367 -0
- package/modules/tui/kernel/pkg/shared/provider-contracts.ts +150 -0
- package/modules/tui/kernel/pkg/shared/provider-display.ts +55 -0
- package/modules/tui/kernel/pkg/shared/provider-parsers.ts +171 -0
- package/modules/tui/kernel/pkg/shared/volcengine.ts +191 -0
- package/modules/tui/kernel/pkg/shared/zhipu.ts +149 -0
- package/modules/tui/kernel/pkg/usage-core/index.ts +335 -0
- package/modules/tui/kernel/pkg/usage-core/provider-routes.ts +187 -0
- package/modules/tui/kernel/pkg/usage-node/index.ts +635 -0
- package/modules/tui/kernel/pkg/usage-node/provider-usage.ts +388 -0
- package/modules/tui/kernel/usage-core.ts +2 -0
- package/modules/tui/kernel/usage-node.ts +2 -0
- package/modules/tui/menu.ts +418 -0
- package/modules/tui/plugin/editor.ts +396 -0
- package/modules/tui/plugin/footer.ts +161 -0
- package/modules/tui/plugin/index.ts +30 -0
- package/modules/tui/plugin/lifecycle.ts +637 -0
- package/modules/tui/plugin/package-order.ts +169 -0
- package/modules/tui/plugin/screen-transition.ts +203 -0
- package/modules/tui/plugin/settings-config.ts +297 -0
- package/modules/tui/plugin/status-sources.ts +49 -0
- package/modules/tui/plugin/transition-gate.ts +261 -0
- package/modules/tui/renderer/custom-header.ts +157 -0
- package/modules/tui/renderer/editor.ts +70 -0
- package/modules/tui/renderer/header.ts +72 -0
- package/modules/tui/renderer/icons.ts +149 -0
- package/modules/tui/renderer/pi-installer-logo.ts +194 -0
- package/modules/tui/status/auto-compaction.ts +67 -0
- package/modules/tui/status/project-status.ts +655 -0
- package/modules/tui/status/provider-status.ts +120 -0
- package/modules/tui/status/runtime-status.ts +307 -0
- package/modules/tui/status/session-status.ts +325 -0
- package/modules/tui/status/status-config.ts +95 -0
- package/modules/tui/status/status-segments.ts +263 -0
- package/modules/tui/status/turn-telemetry.ts +466 -0
- package/modules/tui/themes/LICENSE.pi-themes-bundle +21 -0
- package/modules/tui/themes/UPSTREAM.md +7 -0
- package/modules/tui/themes/catppuccin-latte.json +80 -0
- package/modules/tui/themes/catppuccin-mocha.json +79 -0
- package/modules/tui/themes/crimson-noir.json +85 -0
- package/modules/tui/themes/dracula.json +79 -0
- package/modules/tui/themes/everforest-dark.json +85 -0
- package/modules/tui/themes/gruvbox-dark.json +85 -0
- package/modules/tui/themes/gruvbox-light.json +85 -0
- package/modules/tui/themes/matrix.json +85 -0
- package/modules/tui/themes/nord.json +85 -0
- package/modules/tui/themes/one-dark.json +85 -0
- package/modules/tui/themes/rose-pine-dawn.json +85 -0
- package/modules/tui/themes/rose-pine.json +85 -0
- package/modules/tui/themes/solarized-dark.json +85 -0
- package/modules/tui/themes/solarized-light.json +85 -0
- package/modules/tui/themes/tokyo-night-storm.json +79 -0
- package/modules/tui/themes/tokyo-night.json +79 -0
- package/package.json +28 -0
- package/services.ts +38 -0
- package/toolkit.ts +147 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* herdr 表面 —— IO 原语。语法族判定(shellEscape/sentinelSuffix)由
|
|
3
|
+
* surface.ts 统一分派,本模块只负责对 herdr CLI 的操作。
|
|
4
|
+
*
|
|
5
|
+
* 全部 herdr 交互经 M0 spike 在 Windows 预览版 0.8.2 实证:
|
|
6
|
+
*
|
|
7
|
+
* herdr pane split --pane <id> --direction right|down --cwd <dir> --no-focus
|
|
8
|
+
* → 新 pane,JSON 输出 .result.pane.pane_id(--env KEY=VALUE 可叠加)
|
|
9
|
+
* herdr pane run <pane> <text>
|
|
10
|
+
* → 向 pane 发送一行文本 + Enter(子代理启动命令 / steer 消息)
|
|
11
|
+
* herdr pane read <pane> --source recent-unwrapped --lines N
|
|
12
|
+
* → 读取近期输出(软换行合并,适合日志与对话)
|
|
13
|
+
* herdr pane close <pane>
|
|
14
|
+
* → 关闭 pane
|
|
15
|
+
*
|
|
16
|
+
* 有意不使用的 herdr 能力(决策记录,避免后人误判是遗漏):
|
|
17
|
+
* - agent 命令族(agent start --kind pi / prompt --wait / wait --until):
|
|
18
|
+
* Windows 预览版的 agent start 内部经 PowerShell Start-Process 启动,
|
|
19
|
+
* 不认 npm 的 .cmd shim,pi 无法启动;且 pi 检测规则(remote/pi.toml)
|
|
20
|
+
* 在 Windows 不生效,agent_status 恒为 unknown。子代理状态由本扩展的
|
|
21
|
+
* activity 快照文件自持,不依赖 herdr 的生命周期检测(Linux/macOS 下
|
|
22
|
+
* herdr 可正确识别 pi,herdr 修复 Windows 后可在此切换,不影响上层)。
|
|
23
|
+
* - pane resize / 布局重排:herdr 无布局重排能力;两表面统一不做全窗口
|
|
24
|
+
* 重排(tmux 的 select-layout 也已弃用),布局契约靠 split 几何规则与
|
|
25
|
+
* “主 pane 固定一侧、只在 managed 区域内继续切分”(pane-layout.ts)维持。
|
|
26
|
+
*
|
|
27
|
+
* Pane 由 herdr pane id 标识(如 `wR:p7`)。主 pane 稳定占一侧:首次分屏
|
|
28
|
+
* 从主 pane 切出,之后只在子代理区域内选面积最大的 pane 继续切分
|
|
29
|
+
* (chooseSplitTarget),沿更适合的方向(right/down)切分,不抢用户焦点。
|
|
30
|
+
*/
|
|
31
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
32
|
+
import { promisify } from "node:util";
|
|
33
|
+
import { MIN_PANE_COLS, MIN_PANE_ROWS, rectMeetsBudget, type PaneRect } from "./layout-budget.ts";
|
|
34
|
+
import { chooseSplitDirection, chooseSplitTarget, type LayoutPane, type SplitDirection } from "./pane-layout.ts";
|
|
35
|
+
|
|
36
|
+
const execFileAsync = promisify(execFile);
|
|
37
|
+
|
|
38
|
+
let herdrAvailable: boolean | null = null;
|
|
39
|
+
let herdrCheckedAt = 0;
|
|
40
|
+
const HERDR_RETRY_AFTER_MS = 5_000;
|
|
41
|
+
|
|
42
|
+
// 只记录本扩展创建或恢复接管的 pane,避免把用户自己的 pane 当成布局目标。
|
|
43
|
+
const managedPaneIds = new Set<string>();
|
|
44
|
+
|
|
45
|
+
/** HERDR_ENV=1(pi 在 herdr pane 内)且 herdr CLI 可执行。 */
|
|
46
|
+
export function isAvailable(): boolean {
|
|
47
|
+
if (process.env.HERDR_ENV !== "1") return false;
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
if (herdrAvailable === true) return true;
|
|
50
|
+
if (herdrAvailable === false && now - herdrCheckedAt < HERDR_RETRY_AFTER_MS) return false;
|
|
51
|
+
try {
|
|
52
|
+
execFileSync("herdr", ["--version"], { stdio: "ignore" });
|
|
53
|
+
herdrAvailable = true;
|
|
54
|
+
} catch {
|
|
55
|
+
herdrAvailable = false;
|
|
56
|
+
}
|
|
57
|
+
herdrCheckedAt = now;
|
|
58
|
+
return herdrAvailable;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function setupHint(): string {
|
|
62
|
+
return "Start pi inside a herdr pane (herdr, then run pi in a pane).";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 为子代理创建新 pane:首次从主 pane 切出,之后在子代理区域内继续切分
|
|
67
|
+
* (chooseSplitTarget),不抢焦点。
|
|
68
|
+
* opts.cwd 直接在 split 时指定(等价于上游 tmux 版启动命令里的 cd 前缀);
|
|
69
|
+
* opts.env 的键值经 pane split --env 注入子进程(子代理身份、会话路径等
|
|
70
|
+
* PI_SUBAGENT_* 变量),不拼 shell 前缀。
|
|
71
|
+
* 返回 herdr pane id(如 `wR:p7`)。
|
|
72
|
+
*/
|
|
73
|
+
export function createSurface(
|
|
74
|
+
name: string,
|
|
75
|
+
opts?: { cwd?: string; env?: Record<string, string> },
|
|
76
|
+
): string {
|
|
77
|
+
void name; // herdr pane 靠 terminal title 展示,pane 内 pi 进程自带标题。
|
|
78
|
+
const parentId = process.env.HERDR_PANE_ID;
|
|
79
|
+
if (!parentId) throw new Error("HERDR_PANE_ID is missing; cannot place a subagent pane.");
|
|
80
|
+
|
|
81
|
+
const before = queryTabLayout(parentId);
|
|
82
|
+
const target = chooseSplitTarget(before, parentId, managedPaneIds);
|
|
83
|
+
if (!target) {
|
|
84
|
+
throw new Error("Cannot inspect the current Herdr tab layout for a subagent pane.");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const direction = chooseSplitDirection(target.rect, MIN_PANE_COLS, MIN_PANE_ROWS);
|
|
88
|
+
if (!direction) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Terminal too small for another subagent pane (current pane is ${target.rect.width}x${target.rect.height}, ` +
|
|
91
|
+
`need >= ${MIN_PANE_COLS * 2}x${MIN_PANE_ROWS * 2} for another split).`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const paneId = splitPane(target.pane_id, direction, opts);
|
|
95
|
+
managedPaneIds.add(paneId);
|
|
96
|
+
|
|
97
|
+
const after = queryTabLayout(parentId);
|
|
98
|
+
const relevantIds = new Set([parentId, ...managedPaneIds]);
|
|
99
|
+
const invalidPane = after?.find(
|
|
100
|
+
(pane) => relevantIds.has(pane.pane_id) && !rectMeetsBudget(pane.rect),
|
|
101
|
+
);
|
|
102
|
+
if (!after || invalidPane) {
|
|
103
|
+
managedPaneIds.delete(paneId);
|
|
104
|
+
try { execFileSync("herdr", ["pane", "close", paneId], { encoding: "utf8" }); } catch {}
|
|
105
|
+
const rect = invalidPane?.rect;
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Terminal too small for another subagent pane (got ${rect?.width ?? "?"}x${rect?.height ?? "?"}, ` +
|
|
108
|
+
`need >= ${MIN_PANE_COLS}x${MIN_PANE_ROWS}). Reduce subagents or enlarge the window.`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return paneId;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** 读取当前 Tab 的完整 pane 布局。 */
|
|
115
|
+
function queryTabLayout(paneId: string): LayoutPane[] | null {
|
|
116
|
+
try {
|
|
117
|
+
const parsed = JSON.parse(execFileSync("herdr", ["pane", "layout", "--pane", paneId], { encoding: "utf8" })) as {
|
|
118
|
+
result?: { layout?: { panes?: Array<{ pane_id?: unknown; rect?: { width?: unknown; height?: unknown } }> } };
|
|
119
|
+
};
|
|
120
|
+
return (parsed.result?.layout?.panes ?? []).flatMap((pane) => {
|
|
121
|
+
const paneIdValue = pane.pane_id;
|
|
122
|
+
const width = pane.rect?.width;
|
|
123
|
+
const height = pane.rect?.height;
|
|
124
|
+
return typeof paneIdValue === "string" && typeof width === "number" && typeof height === "number"
|
|
125
|
+
? [{ pane_id: paneIdValue, rect: { width, height } }]
|
|
126
|
+
: [];
|
|
127
|
+
});
|
|
128
|
+
} catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function splitPane(
|
|
134
|
+
target: string,
|
|
135
|
+
direction: SplitDirection,
|
|
136
|
+
opts?: { cwd?: string; env?: Record<string, string> },
|
|
137
|
+
): string {
|
|
138
|
+
const args = ["pane", "split", "--pane", target, "--direction", direction, "--ratio", "0.5", "--no-focus"];
|
|
139
|
+
if (opts?.cwd) args.push("--cwd", opts.cwd);
|
|
140
|
+
for (const [key, value] of Object.entries(opts?.env ?? {})) {
|
|
141
|
+
args.push("--env", `${key}=${value}`);
|
|
142
|
+
}
|
|
143
|
+
const stdout = execFileSync("herdr", args, { encoding: "utf8" });
|
|
144
|
+
const parsed = JSON.parse(stdout);
|
|
145
|
+
const paneId = parsed?.result?.pane?.pane_id;
|
|
146
|
+
if (typeof paneId !== "string" || !paneId) {
|
|
147
|
+
throw new Error(`Unexpected herdr pane split output: ${stdout.slice(0, 200)}`);
|
|
148
|
+
}
|
|
149
|
+
return paneId;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 向 pane 发送一行文本并回车。文本经 execFile 参数直传 herdr,不经过中间
|
|
154
|
+
* shell 解释;pane 内若是 pi TUI 则进入输入框,若是 shell 则作为命令执行。
|
|
155
|
+
*/
|
|
156
|
+
export function sendCommand(surface: string, text: string): void {
|
|
157
|
+
execFileSync("herdr", ["pane", "run", surface, text], { encoding: "utf8" });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** 读取 pane 近期输出(同步)。recent-unwrapped 把软换行合并为单行。 */
|
|
161
|
+
export function readScreen(surface: string, lines = 50): string {
|
|
162
|
+
return execFileSync(
|
|
163
|
+
"herdr",
|
|
164
|
+
["pane", "read", surface, "--source", "recent-unwrapped", "--lines", String(Math.max(1, lines))],
|
|
165
|
+
{ encoding: "utf8" },
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** 读取 pane 近期输出(异步)。 */
|
|
170
|
+
export async function readScreenAsync(surface: string, lines = 50): Promise<string> {
|
|
171
|
+
const { stdout } = await execFileAsync(
|
|
172
|
+
"herdr",
|
|
173
|
+
["pane", "read", surface, "--source", "recent-unwrapped", "--lines", String(Math.max(1, lines))],
|
|
174
|
+
{ encoding: "utf8" },
|
|
175
|
+
);
|
|
176
|
+
return stdout;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** 查询 pane 是否仍然存在,用于 /reload 恢复时丢弃 stale 记录。 */
|
|
180
|
+
export function surfaceExists(surface: string): boolean {
|
|
181
|
+
try {
|
|
182
|
+
execFileSync("herdr", ["pane", "get", surface], { stdio: "ignore" });
|
|
183
|
+
return true;
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** 重载恢复后,把已有 pane 纳入当前 Tab 的布局目标集合。 */
|
|
190
|
+
export function trackSurface(surface: string): void {
|
|
191
|
+
managedPaneIds.add(surface);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** 关闭 pane(其内进程随之终止)。 */
|
|
195
|
+
export function closeSurface(surface: string): void {
|
|
196
|
+
execFileSync("herdr", ["pane", "close", surface], { encoding: "utf8" });
|
|
197
|
+
managedPaneIds.delete(surface);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** 给 pane 设置可识别标签(best-effort,失败不致命)。 */
|
|
201
|
+
export function labelSurface(surface: string, label: string): void {
|
|
202
|
+
execFileSync("herdr", ["pane", "rename", surface, label], { encoding: "utf8" });
|
|
203
|
+
}
|