Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 1,
"providerId": "official",
"name": "PI-Desktop Official Plugins",
"updatedAt": "2026-09-14T04:38:39Z",
"updatedAt": "2026-09-16T04:56:31Z",
"homepage": "https://github.com/vastsa/pi-desktop-plugins",
"plugins": [
{
Expand Down Expand Up @@ -929,17 +929,17 @@
"downloads": 0,
"homepage": "https://github.com/vastsa/pi-desktop-plugins/tree/main/plugins/pi.terminal",
"repository": "https://github.com/vastsa/pi-desktop-plugins",
"readmeMarkdown": "# Terminal \u00b7 \u7ec8\u7aef\n\nOtty-inspired interactive terminal for PI-Desktop. It docks in the right work panel next to the conversation \u2014 there is no separate window.\n\n\u53d7 Otty \u542f\u53d1\u7684\u4ea4\u4e92\u5f0f\u7ec8\u7aef\uff0c\u53ea\u505c\u9760\u5728\u53f3\u4fa7\u5de5\u4f5c\u9762\u677f\uff08\u65e0\u72ec\u7acb\u7a97\u53e3\uff09\u3002\n\n## What it does\n\n- **Work panel view** \u2014 `Terminal` / `\u7ec8\u7aef` in the plugin views menu (icon `terminal`). Open the work panel with **Mod+J**.\n- **Tabs** \u2014 new / close / switch. Closing the view does **not** kill the shell; reopening replays a bounded in-memory scrollback. Tabs belong to the **currently open project**; switching projects shows that project's terminals only (other shells stay alive in the background until the plugin is disabled).\n- **Cross-platform shells**\n - macOS: `$SHELL` \u2192 `/bin/zsh` as a real login shell (`argv0` `-zsh`)\n - Linux: `$SHELL` \u2192 `/bin/bash` as a real login shell\n - Windows: PowerShell \u2192 `cmd.exe`; Git Bash is offered when installed\n- **Login PATH** \u2014 GUI apps often see a thin PATH. On load the plugin dumps your interactive login environment and injects it into every PTY, then prepends common tool dirs (pnpm, nvm, bun, cargo, Homebrew) if they exist. `node` / `pnpm` / `brew` should resolve the same way they do in Terminal.app.\n- Follows the app language (`en` / `zh-CN`) and color mode, including live light/dark switches while the view stays open (chrome and xterm canvas). Copy/paste: macOS `Cmd+C/V`, Windows/Linux `Ctrl+Shift+C/V`. Find: `Cmd/Ctrl+F`. Font size: `Cmd/Ctrl +` / `-`.\n\nNot in v0.1: pane splits, Recipes, SSH sessions (use **SSH Manager**), or an agent tool that types into the PTY.\n\n## Permissions\n\n| Permission | Why |\n| --- | --- |\n| `ui.view` | Work-panel Terminal view |\n| `background.service` | Keep the plugin process (and live PTYs) resident when the UI is closed |\n| `clipboard.read` / `clipboard.write` | Paste / copy |\n| `agent.prompt.inject` | Skill that tells the AI this PTY is for humans; host Bash stays the command path |\n\nNo `fs.*` permission. The plugin process reads the current workspace path and passes it to the helper as the session cwd.\n\n## How it works\n\nA bundled helper binary (`vendor/pi-pty-<os>-<arch>`) owns the real PTY (forkpty / ConPTY). The plugin process speaks a JSON-line protocol with the helper. The sandboxed view has no Node and no push channel, so output is pulled with `pty.drain` long-polls.\n\nOn Windows the helper is a GUI-subsystem binary and attaches PowerShell (or cmd / Git Bash) to ConPTY with `CREATE_NO_WINDOW`, so the shell runs inside the work-panel xterm instead of popping a separate console.\n\nThe zip packer does not preserve unix `+x`. On first spawn the plugin `chmod`s the helper.\n\n## Settings\n\n- `fontSize` \u2014 10\u201324, default 13\n- `scrollback` \u2014 in-memory reconnect buffer in lines, default 5000\n- `maxSessions` \u2014 live tab cap, default 8\n- `profiles` \u2014 JSON array of `{ id, name, shell, args, cwd, env }`. `cwd` must stay inside the workspace or home directory.\n\n## Development\n\n```bash\n# rebuild helpers (Go 1.22+). Windows binaries use -H windowsgui.\nsh plugins/pi.terminal/helper/build.sh\n\npython3 scripts/pack_plugin.py plugins/pi.terminal\npython3 scripts/rebuild_catalog.py\nnode --test tests/terminal.test.mjs tests/panel-chrome.test.mjs\n```\n\nLoad `plugins/pi.terminal` as a development plugin, then open **Terminal** from the work panel (Mod+J).\n\n## Requirements\n\n- PI-Desktop `>= 0.8.0` (`ui.view` / work-panel plugin views)\n- A helper binary for the current `process.platform` + `process.arch`\n",
"readmeMarkdown": "# Terminal \u00b7 \u7ec8\u7aef\n\nOtty-inspired interactive terminal for PI-Desktop. It docks in the right work panel next to the conversation \u2014 there is no separate window.\n\n\u53d7 Otty \u542f\u53d1\u7684\u4ea4\u4e92\u5f0f\u7ec8\u7aef\uff0c\u53ea\u505c\u9760\u5728\u53f3\u4fa7\u5de5\u4f5c\u9762\u677f\uff08\u65e0\u72ec\u7acb\u7a97\u53e3\uff09\u3002\n\n## What it does\n\n- **Work panel view** \u2014 `Terminal` / `\u7ec8\u7aef` in the plugin views menu (icon `terminal`). Open the work panel with **Mod+J**.\n- **Tabs** \u2014 new / close / switch. Closing the view does **not** kill the shell; reopening replays a bounded in-memory scrollback. Tabs belong to the **currently open project**; switching projects shows that project's terminals only (other shells stay alive in the background until the plugin is disabled).\n- **Cross-platform shells**\n - macOS: `$SHELL` \u2192 `/bin/zsh` as a real login shell (`argv0` `-zsh`)\n - Linux: `$SHELL` \u2192 `/bin/bash` as a real login shell\n - Windows: PowerShell \u2192 `cmd.exe`; Git Bash is offered when installed\n- **Login PATH** \u2014 GUI apps often see a thin PATH. On load the plugin dumps your interactive login environment and injects it into every PTY, then prepends common tool dirs (pnpm, nvm, bun, cargo, Homebrew) if they exist. `node` / `pnpm` / `brew` should resolve the same way they do in Terminal.app.\n- Follows the app language (`en` / `zh-CN`) and color mode, including live light/dark switches while the view stays open (chrome and xterm canvas). Copy/paste: macOS `Cmd+C/V`, Windows/Linux `Ctrl+Shift+C/V`. Find: `Cmd/Ctrl+F`. Font size: `Cmd/Ctrl +` / `-`.\n\nNot in v0.1: pane splits, Recipes, SSH sessions (use **SSH Manager**), or an agent tool that types into the PTY.\n\n## Permissions\n\n| Permission | Why |\n| --- | --- |\n| `ui.view` | Work-panel Terminal view |\n| `background.service` | Keep the plugin process (and live PTYs) resident when the UI is closed |\n| `clipboard.read` / `clipboard.write` | Paste / copy |\n| `agent.prompt.inject` | Skill that tells the AI this PTY is for humans; host Bash stays the command path |\n\nNo `fs.*` permission. The plugin process reads the current workspace path and passes it to the helper as the session cwd.\n\n## How it works\n\nA bundled helper binary (`vendor/pi-pty-<os>-<arch>`) owns the real PTY (forkpty / ConPTY). The plugin process speaks a JSON-line protocol with the helper. The sandboxed view has no Node and no push channel, so output is pulled with `pty.drain` long-polls.\n\nOn Windows the helper is a GUI-subsystem binary and attaches PowerShell (or cmd / Git Bash) to ConPTY by passing the HPCON through `PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE`, so the shell runs inside the work-panel xterm instead of popping a separate console. Do not add `CREATE_NO_WINDOW` to the `CreateProcess` flags: it detaches the child from the pseudoconsole, which then silently keeps its own hidden console and leaves the xterm blank.\n\nThe zip packer does not preserve unix `+x`. On first spawn the plugin `chmod`s the helper.\n\n## Settings\n\n- `fontSize` \u2014 10\u201324, default 13\n- `scrollback` \u2014 in-memory reconnect buffer in lines, default 5000\n- `maxSessions` \u2014 live tab cap, default 8\n- `profiles` \u2014 JSON array of `{ id, name, shell, args, cwd, env }`. `cwd` must stay inside the workspace or home directory.\n\n## Development\n\n```bash\n# rebuild helpers (Go 1.22+). Windows binaries use -H windowsgui.\nsh plugins/pi.terminal/helper/build.sh\n\npython3 scripts/pack_plugin.py plugins/pi.terminal\npython3 scripts/rebuild_catalog.py\nnode --test tests/terminal.test.mjs tests/panel-chrome.test.mjs\n```\n\nLoad `plugins/pi.terminal` as a development plugin, then open **Terminal** from the work panel (Mod+J).\n\n## Requirements\n\n- PI-Desktop `>= 0.8.0` (`ui.view` / work-panel plugin views)\n- A helper binary for the current `process.platform` + `process.arch`\n",
"safetyNotes": "This plugin launches a real login shell as your user through a bundled PTY helper (forkpty on macOS/Linux, ConPTY on Windows). Anything you can run in a system terminal can run here, including destructive commands. It does not persist transcripts, passwords, or shell history beyond an in-memory scrollback that dies with the plugin process. Uninstalling or disabling the plugin kills every session. The AI is not given a tool that writes to the PTY; agent command execution continues to use the host Bash tool.",
"versions": [
{
"version": "0.1.6",
"publishedAt": "2026-09-12T17:13:25Z",
"changelog": "Release 0.1.6: on Windows, attach PowerShell to ConPTY inside the work panel instead of opening a separate console window. Release 0.1.5: follow the app color mode only \u2014 never snap to the OS palette, which flashed when the two disagreed. Release 0.1.4: follows the app light/dark mode live in the work-panel view, including the xterm canvas. Release 0.1.3: isolates PTY tabs per open project so switching workspaces cannot mix sessions. Release 0.1.2: docks only in the right work panel; the detached window is removed. Release 0.1.1: login-shell environment so GUI-launched sessions inherit PATH (pnpm, nvm, Homebrew, cargo, bun); denser Otty-like chrome with bottom tabs, find, and font-size controls. Release 0.1.0: first release. Multi-tab PTY sessions, cross-platform default shells (zsh / bash / PowerShell / optional Git Bash), theme and language following, and copy/paste.",
"version": "0.1.7",
"publishedAt": "2026-09-16T04:56:31Z",
"changelog": "Release 0.1.7: stop passing CREATE_NO_WINDOW to the Windows helper CreateProcess call. That flag detached the child from the pseudoconsole, so the shell silently kept its own hidden console and the work-panel xterm stayed blank with no prompt or echo (issue #13). Release 0.1.6: on Windows, attach PowerShell to ConPTY inside the work panel instead of opening a separate console window. Release 0.1.5: follow the app color mode only \u2014 never snap to the OS palette, which flashed when the two disagreed. Release 0.1.4: follows the app light/dark mode live in the work-panel view, including the xterm canvas. Release 0.1.3: isolates PTY tabs per open project so switching workspaces cannot mix sessions. Release 0.1.2: docks only in the right work panel; the detached window is removed. Release 0.1.1: login-shell environment so GUI-launched sessions inherit PATH (pnpm, nvm, Homebrew, cargo, bun); denser Otty-like chrome with bottom tabs, find, and font-size controls. Release 0.1.0: first release. Multi-tab PTY sessions, cross-platform default shells (zsh / bash / PowerShell / optional Git Bash), theme and language following, and copy/paste.",
"minPiDesktop": ">=0.8.0",
"shasum": "e915e96f6895a3f3881314e29099ae3c59236e8e93e6f805f4ba6d3f14919c22",
"url": "packages/pi.terminal-0.1.6.piplug",
"sizeBytes": 13952827,
"shasum": "433db0e69e3ae4b6889165131fdd622369d050af7eb32fde05456fd004d0d6f1",
"url": "packages/pi.terminal-0.1.7.piplug",
"sizeBytes": 13787119,
"permissions": [
"ui.view",
"background.service",
Expand Down
Binary file added packages/pi.terminal-0.1.7.piplug
Binary file not shown.
2 changes: 1 addition & 1 deletion plugins/pi.terminal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ No `fs.*` permission. The plugin process reads the current workspace path and pa

A bundled helper binary (`vendor/pi-pty-<os>-<arch>`) owns the real PTY (forkpty / ConPTY). The plugin process speaks a JSON-line protocol with the helper. The sandboxed view has no Node and no push channel, so output is pulled with `pty.drain` long-polls.

On Windows the helper is a GUI-subsystem binary and attaches PowerShell (or cmd / Git Bash) to ConPTY with `CREATE_NO_WINDOW`, so the shell runs inside the work-panel xterm instead of popping a separate console.
On Windows the helper is a GUI-subsystem binary and attaches PowerShell (or cmd / Git Bash) to ConPTY by passing the HPCON through `PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE`, so the shell runs inside the work-panel xterm instead of popping a separate console. Do not add `CREATE_NO_WINDOW` to the `CreateProcess` flags: it detaches the child from the pseudoconsole, which then silently keeps its own hidden console and leaves the xterm blank.

The zip packer does not preserve unix `+x`. On first spawn the plugin `chmod`s the helper.

Expand Down
5 changes: 4 additions & 1 deletion plugins/pi.terminal/helper/pty_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,10 @@ func startPty(req request) (*ptySession, error) {
nil,
nil,
false,
windows.CREATE_UNICODE_ENVIRONMENT|windows.EXTENDED_STARTUPINFO_PRESENT|windows.CREATE_NO_WINDOW,
// CREATE_NO_WINDOW would detach the child from the pseudoconsole: Windows
// then gives it its own hidden console (120x9001 by default) and the ConPTY
// pipes stay silent, so the work-panel xterm never sees a prompt.
windows.CREATE_UNICODE_ENVIRONMENT | windows.EXTENDED_STARTUPINFO_PRESENT,
&envUTF16[0],
cwdPtr,
&si.StartupInfo,
Expand Down
4 changes: 2 additions & 2 deletions plugins/pi.terminal/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"schemaVersion": 1,
"id": "pi.terminal",
"name": "Terminal",
"version": "0.1.6",
"version": "0.1.7",
"description": "Otty-inspired interactive terminal for PI-Desktop. Docks in the right work panel (no separate window), with tabs, cross-platform shell profiles, and copy/paste.",
"i18n": {
"en": {
Expand All @@ -18,7 +18,7 @@
},
"author": "PI-Desktop",
"categories": ["developer-tools", "official"],
"changelog": "Release 0.1.6: on Windows, attach PowerShell to ConPTY inside the work panel instead of opening a separate console window. Release 0.1.5: follow the app color mode only — never snap to the OS palette, which flashed when the two disagreed. Release 0.1.4: follows the app light/dark mode live in the work-panel view, including the xterm canvas. Release 0.1.3: isolates PTY tabs per open project so switching workspaces cannot mix sessions. Release 0.1.2: docks only in the right work panel; the detached window is removed. Release 0.1.1: login-shell environment so GUI-launched sessions inherit PATH (pnpm, nvm, Homebrew, cargo, bun); denser Otty-like chrome with bottom tabs, find, and font-size controls. Release 0.1.0: first release. Multi-tab PTY sessions, cross-platform default shells (zsh / bash / PowerShell / optional Git Bash), theme and language following, and copy/paste.",
"changelog": "Release 0.1.7: stop passing CREATE_NO_WINDOW to the Windows helper CreateProcess call. That flag detached the child from the pseudoconsole, so the shell silently kept its own hidden console and the work-panel xterm stayed blank with no prompt or echo (issue #13). Release 0.1.6: on Windows, attach PowerShell to ConPTY inside the work panel instead of opening a separate console window. Release 0.1.5: follow the app color mode only — never snap to the OS palette, which flashed when the two disagreed. Release 0.1.4: follows the app light/dark mode live in the work-panel view, including the xterm canvas. Release 0.1.3: isolates PTY tabs per open project so switching workspaces cannot mix sessions. Release 0.1.2: docks only in the right work panel; the detached window is removed. Release 0.1.1: login-shell environment so GUI-launched sessions inherit PATH (pnpm, nvm, Homebrew, cargo, bun); denser Otty-like chrome with bottom tabs, find, and font-size controls. Release 0.1.0: first release. Multi-tab PTY sessions, cross-platform default shells (zsh / bash / PowerShell / optional Git Bash), theme and language following, and copy/paste.",
"safetyNotes": "This plugin launches a real login shell as your user through a bundled PTY helper (forkpty on macOS/Linux, ConPTY on Windows). Anything you can run in a system terminal can run here, including destructive commands. It does not persist transcripts, passwords, or shell history beyond an in-memory scrollback that dies with the plugin process. Uninstalling or disabling the plugin kills every session. The AI is not given a tool that writes to the PTY; agent command execution continues to use the host Bash tool.",
"main": "main.js",
"contributes": {
Expand Down
Binary file modified plugins/pi.terminal/vendor/pi-pty-win32-arm64.exe
Binary file not shown.
Binary file modified plugins/pi.terminal/vendor/pi-pty-win32-x64.exe
Binary file not shown.
13 changes: 11 additions & 2 deletions tests/terminal.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ function waitFor(fn, timeoutMs = 1500) {
test("manifest declares terminal identity, views, permissions and no agent tools", () => {
assert.equal(manifest.schemaVersion, 1);
assert.equal(manifest.id, "pi.terminal");
assert.equal(manifest.version, "0.1.6");
assert.equal(manifest.version, "0.1.7");
assert.equal(manifest.ui, undefined);
assert.deepEqual(manifest.permissions, [
"ui.view",
Expand Down Expand Up @@ -160,7 +160,16 @@ test("Windows ConPTY helper attaches the shell instead of opening a console wind
const ptyJs = readFileSync(join(pluginRoot, "pty.js"), "utf8");
assert.match(winPty, /windows\.CreatePseudoConsole/);
assert.match(winPty, /PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE/);
assert.match(winPty, /CREATE_NO_WINDOW/);
// Comments may name the flag to explain why it must stay out; only code matters.
const winPtyCode = winPty
.split("\n")
.filter((line) => !line.trim().startsWith("//"))
.join("\n");
assert.doesNotMatch(
winPtyCode,
/CREATE_NO_WINDOW/,
"CREATE_NO_WINDOW detaches the child from the pseudoconsole and leaves the work-panel xterm blank",
);
assert.match(winPty, /STARTF_USESTDHANDLES/);
assert.match(winPty, /STARTF_USESHOWWINDOW/);
assert.match(winPty, /SW_HIDE/);
Expand Down