pi-tinychat 0.1.0 → 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/LICENSE +21 -21
- package/README.md +139 -139
- package/bin/pi-tinychat.js +111 -111
- package/extension/plan.ts +281 -240
- package/package.json +68 -68
- package/public/app.js +3749 -3614
- package/public/favicon.svg +15 -15
- package/public/i18n.js +197 -169
- package/public/index.html +226 -226
- package/public/markdown.js +244 -244
- package/public/style.css +2814 -2762
- package/public/theme-init.js +18 -18
- package/src/compaction.js +139 -139
- package/src/errors.js +13 -13
- package/src/live.js +133 -133
- package/src/pi-path.js +70 -70
- package/src/pi-types.d.ts +85 -85
- package/src/pi.js +196 -196
- package/src/plan.js +91 -90
- package/src/server.js +910 -910
- package/src/sessions.js +256 -256
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 VarYU
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VarYU
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
<p>
|
|
2
|
-
<img src="https://raw.githubusercontent.com/VarYUvrc/pi-tinychat/main/public/favicon.svg" width="48" height="48" alt="Pi TinyChat icon" />
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
# Pi TinyChat
|
|
6
|
-
|
|
7
|
-
**Tiny UI. Full Pi. Zero Bloat.**
|
|
8
|
-
|
|
9
|
-
[](https://www.npmjs.com/package/pi-tinychat)
|
|
10
|
-
[
|
|
13
|
-
|
|
14
|
-
Pi TinyChat is a local browser window for the [Pi coding agent](https://pi.dev). The conversation stays on the left and the plan stays on the right.
|
|
15
|
-
|
|
16
|
-
It is for people who like Pi in a terminal but want a calmer screen for long tasks — without adding a second app to babysit.
|
|
17
|
-
|
|
18
|
-
> Pi TinyChat is an independent community project. It is not an official Pi project and is not affiliated with the Pi maintainers.
|
|
19
|
-
|
|
20
|
-

|
|
21
|
-
|
|
22
|
-
## What you get
|
|
23
|
-
|
|
24
|
-
- **A plan you can glance at.** The agent publishes a checklist, and the right panel shows done, current, and pending items while it works.
|
|
25
|
-
- **Context control you can actually feel.** Set auto-compaction at 25%, 50%, or 75% of the model window, or leave Pi's default. See the context ring and the exact reserve before the next long turn. Long-context models often get less precise well before the window is full, so 25–50% is a practical range. Open the ring to switch presets, or run `/compact` from the + menu to compact manually.
|
|
26
|
-
- **Sessions that keep running.** Open another conversation without stopping the one in flight. Each session has its own Pi process.
|
|
27
|
-
- **A conversation made for reading.** Streaming Markdown, compact thinking and tool cards, image attachments, and extension dialogs.
|
|
28
|
-
- **Your Pi setup.** Login, models, reasoning effort, context usage, auto-compaction, and session history come from Pi itself.
|
|
29
|
-
- **A small stack.** Plain ES modules in the browser, JSONL over Pi RPC, no React, no bundler, no database, and no runtime dependencies.
|
|
30
|
-
|
|
31
|
-
## Quick start
|
|
32
|
-
|
|
33
|
-
You need **Node.js 22.19 or newer** and Pi. Pi TinyChat runs Pi in the current directory, so start it from the project you want to work in:
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
npx pi-tinychat
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Use `--cwd /path/to/project` when you start from another directory.
|
|
40
|
-
|
|
41
|
-
Or install the command globally:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
npm install -g pi-tinychat
|
|
45
|
-
pi-tinychat
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
The browser opens at `http://127.0.0.1:4310`. If that port is taken, Pi TinyChat automatically uses the next free port. Use `--port` to start from another port, and `--no-open` when you do not want the browser to open automatically.
|
|
49
|
-
|
|
50
|
-
Authenticate once in a terminal; Pi TinyChat reuses that login:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
pi
|
|
54
|
-
/login
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
If you use API keys instead of `/login`, set the provider key in the shell that launches Pi TinyChat, for example `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENCODE_API_KEY`, `DEEPSEEK_API_KEY`, `GEMINI_API_KEY`, or `OPENROUTER_API_KEY`.
|
|
58
|
-
|
|
59
|
-
The npm package is published with the `pi-package` keyword, which is what the Pi package catalog uses for discovery.
|
|
60
|
-
|
|
61
|
-
## The plan panel
|
|
62
|
-
|
|
63
|
-
Pi TinyChat includes a small Pi extension that gives the agent a `tiny_plan` tool. For a multi-step task, the agent writes
|
|
64
|
-
|
|
65
|
-
The right panel mirrors that plan immediately and stores it in the Pi session, so reopening or resuming the conversation brings the checklist back. It is the fastest way to see what the agent is doing, what is finished, and what is left.
|
|
66
|
-
|
|
67
|
-
## Options
|
|
68
|
-
|
|
69
|
-
```text
|
|
70
|
-
--cwd <path> Working directory for Pi (default: current directory)
|
|
71
|
-
--port <port> Start at this port on 127.0.0.1 (default: $PI_TINYCHAT_PORT or 4310; falls back to the next free port)
|
|
72
|
-
--no-open Do not open the browser automatically
|
|
73
|
-
--pi <path> Path to the pi executable or CLI entry point
|
|
74
|
-
--continue Resume the most recent Pi session in the directory
|
|
75
|
-
--help Show help
|
|
76
|
-
--version Show the version
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Security
|
|
80
|
-
|
|
81
|
-
Pi TinyChat is local-first by design:
|
|
82
|
-
|
|
83
|
-
- **No runtime dependencies and no install scripts.** The npm package ships plain JavaScript and one Pi extension; it does not add a dependency tree to your project.
|
|
84
|
-
- **Localhost only.** The server binds to `127.0.0.1`, checks the Host and Origin headers, and requires a per-launch token.
|
|
85
|
-
- **Launch token, then cookie.** The token in the launch URL is exchanged for an `HttpOnly`, `SameSite=Strict` cookie, and the URL is cleaned up.
|
|
86
|
-
- **Strict browser policy.** Static files are served with a Content Security Policy, `nosniff`, and no-store caching. Markdown is escaped before rendering.
|
|
87
|
-
- **Reviewable releases.** `npm publish` runs lint, typecheck, i18n checks, a real-Pi offline integration test, and a tarball check. GitHub Actions publishes with npm provenance.
|
|
88
|
-
- **No telemetry.** Pi TinyChat does not send usage data anywhere.
|
|
89
|
-
|
|
90
|
-
## How it works
|
|
91
|
-
|
|
92
|
-
- **Local first.** Conversations and settings use Pi's files. UI preferences live in `localStorage`; there is no separate database or log store.
|
|
93
|
-
- **Pi-native state.** Login, models, reasoning effort, context usage, and compaction settings come from Pi.
|
|
94
|
-
- **One process per session.** Background sessions stay alive, so switching conversations does not interrupt running work.
|
|
95
|
-
- **Official RPC.** Pi runs as a separate `pi --mode rpc` process and speaks JSONL. Agent behavior stays with Pi.
|
|
96
|
-
|
|
97
|
-
## Other Pi UIs
|
|
98
|
-
|
|
99
|
-
| UI | Best for |
|
|
100
|
-
| --- | --- |
|
|
101
|
-
| **Pi TinyChat** | A small local chat with a live Plan panel and context compaction presets. No build step or runtime dependencies. |
|
|
102
|
-
| [pi-web](https://github.com/agegr/pi-web) | A full web workspace: session branching, Git worktrees, file browsing, MCP/model/skill management, subagents, and mobile. |
|
|
103
|
-
| [pi-web-simple](https://pi.dev/packages/pi-web-simple) | A simple RPC-based web UI with Git and file management. |
|
|
104
|
-
| [pipane](https://github.com/mike-heunher/pipane) | Another RPC-based browser UI for Pi. |
|
|
105
|
-
|
|
106
|
-
## Troubleshooting
|
|
107
|
-
|
|
108
|
-
- **`pi` is not found** — pass `--pi /path/to/pi` or set `PI_TINYCHAT_PI`.
|
|
109
|
-
- **The model is `unknown` or sending fails** — run `/login` in a terminal, or set a provider API key in the shell that launches Pi TinyChat. Check with `pi auth check --provider <provider>`.
|
|
110
|
-
- **Project `.pi` settings have no effect** — run `pi` once in that project and execute `/trust`. RPC mode follows Pi's trust rules.
|
|
111
|
-
- **History is empty or shows another project** — make sure `--cwd` matches the directory you use with Pi. Set `PI_CODING_AGENT_DIR` and `PI_CODING_AGENT_SESSION_DIR` in the same environment if you customize them.
|
|
112
|
-
- **The port is in use** — Pi TinyChat automatically tries the next 20 ports. Pass `--port` to choose the starting port.
|
|
113
|
-
|
|
114
|
-
## Compatibility
|
|
115
|
-
|
|
116
|
-
| Item | Detail |
|
|
117
|
-
| --- | --- |
|
|
118
|
-
| Pi version | Tested with **1.4.0**; CI also checks the latest weekly Pi release. |
|
|
119
|
-
| Node.js | **22.19** or newer. |
|
|
120
|
-
| OS | Windows 10/11, macOS, and Linux (including WSL). |
|
|
121
|
-
| Terminal-first features | `/login`, `/tree`, `/fork`, `/clone`, and TUI-only custom UI stay in the Pi terminal. |
|
|
122
|
-
| Project `.pi` | Follows Pi's trust rules. Run `/trust` once in the project before starting Pi TinyChat. |
|
|
123
|
-
| Resources | Each open session keeps a Pi process alive, so memory and CPU use grow with the number of sessions. |
|
|
124
|
-
| Multiple tabs | Session switching is not synchronized across browser tabs. |
|
|
125
|
-
|
|
126
|
-
## Development
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
npm ci
|
|
130
|
-
npm link # makes the `pi-tinychat` command available locally
|
|
131
|
-
pi-tinychat # starts Pi in this repository
|
|
132
|
-
npm run check # lint, types, i18n, E2E, tarball check
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Individual checks and the manual real-model smoke test are defined in `package.json`. CI runs on Node 22.19 on Ubuntu and Windows, with a weekly check against the latest Pi.
|
|
136
|
-
|
|
137
|
-
## License
|
|
138
|
-
|
|
139
|
-
[MIT](https://github.com/VarYUvrc/pi-tinychat/blob/main/LICENSE)
|
|
1
|
+
<p>
|
|
2
|
+
<img src="https://raw.githubusercontent.com/VarYUvrc/pi-tinychat/main/public/favicon.svg" width="48" height="48" alt="Pi TinyChat icon" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Pi TinyChat
|
|
6
|
+
|
|
7
|
+
**Tiny UI. Full Pi. Zero Bloat.**
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/pi-tinychat)
|
|
10
|
+
[](https://github.com/VarYUvrc/pi-tinychat/blob/main/LICENSE)
|
|
11
|
+
|
|
12
|
+
English | [日本語](docs/README.ja.md)
|
|
13
|
+
|
|
14
|
+
Pi TinyChat is a local browser window for the [Pi coding agent](https://pi.dev). The conversation stays on the left and the plan stays on the right.
|
|
15
|
+
|
|
16
|
+
It is for people who like Pi in a terminal but want a calmer screen for long tasks — without adding a second app to babysit.
|
|
17
|
+
|
|
18
|
+
> Pi TinyChat is an independent community project. It is not an official Pi project and is not affiliated with the Pi maintainers.
|
|
19
|
+
|
|
20
|
+

|
|
21
|
+
|
|
22
|
+
## What you get
|
|
23
|
+
|
|
24
|
+
- **A plan you can glance at.** The agent publishes a checklist, and the right panel shows done, current, and pending items while it works. A short progress note in the user's language appears in the chat when the agent changes phase.
|
|
25
|
+
- **Context control you can actually feel.** Set auto-compaction at 25%, 50%, or 75% of the model window, or leave Pi's default. See the context ring and the exact reserve before the next long turn. Long-context models often get less precise well before the window is full, so 25–50% is a practical range. Open the ring to switch presets, or run `/compact` from the + menu to compact manually.
|
|
26
|
+
- **Sessions that keep running.** Open another conversation without stopping the one in flight. Each session has its own Pi process.
|
|
27
|
+
- **A conversation made for reading.** Streaming Markdown, compact thinking and tool cards, image attachments, and extension dialogs.
|
|
28
|
+
- **Your Pi setup.** Login, models, reasoning effort, context usage, auto-compaction, and session history come from Pi itself.
|
|
29
|
+
- **A small stack.** Plain ES modules in the browser, JSONL over Pi RPC, no React, no bundler, no database, and no runtime dependencies.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
You need **Node.js 22.19 or newer** and Pi. Pi TinyChat runs Pi in the current directory, so start it from the project you want to work in:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx pi-tinychat
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `--cwd /path/to/project` when you start from another directory.
|
|
40
|
+
|
|
41
|
+
Or install the command globally:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g pi-tinychat
|
|
45
|
+
pi-tinychat
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The browser opens at `http://127.0.0.1:4310`. If that port is taken, Pi TinyChat automatically uses the next free port. Use `--port` to start from another port, and `--no-open` when you do not want the browser to open automatically.
|
|
49
|
+
|
|
50
|
+
Authenticate once in a terminal; Pi TinyChat reuses that login:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pi
|
|
54
|
+
/login
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
If you use API keys instead of `/login`, set the provider key in the shell that launches Pi TinyChat, for example `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `OPENCODE_API_KEY`, `DEEPSEEK_API_KEY`, `GEMINI_API_KEY`, or `OPENROUTER_API_KEY`.
|
|
58
|
+
|
|
59
|
+
The npm package is published with the `pi-package` keyword, which is what the Pi package catalog uses for discovery.
|
|
60
|
+
|
|
61
|
+
## The plan panel
|
|
62
|
+
|
|
63
|
+
Pi TinyChat includes a small Pi extension that gives the agent a `tiny_plan` tool. For a multi-step task, the agent writes a short provisional plan before the first substantive tool call, then marks each step in progress and done as it goes. The plan keeps its current granularity; the agent uses a one-sentence `note` for the current focus between steps instead of adding micro-steps.
|
|
64
|
+
|
|
65
|
+
The right panel mirrors that plan immediately and stores it in the Pi session, so reopening or resuming the conversation brings the checklist back. The note is shown as a live status line in the chat, while tool activity fills in the finer detail without extra model calls. It is the fastest way to see what the agent is doing, what is finished, and what is left.
|
|
66
|
+
|
|
67
|
+
## Options
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
--cwd <path> Working directory for Pi (default: current directory)
|
|
71
|
+
--port <port> Start at this port on 127.0.0.1 (default: $PI_TINYCHAT_PORT or 4310; falls back to the next free port)
|
|
72
|
+
--no-open Do not open the browser automatically
|
|
73
|
+
--pi <path> Path to the pi executable or CLI entry point
|
|
74
|
+
--continue Resume the most recent Pi session in the directory
|
|
75
|
+
--help Show help
|
|
76
|
+
--version Show the version
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Security
|
|
80
|
+
|
|
81
|
+
Pi TinyChat is local-first by design:
|
|
82
|
+
|
|
83
|
+
- **No runtime dependencies and no install scripts.** The npm package ships plain JavaScript and one Pi extension; it does not add a dependency tree to your project.
|
|
84
|
+
- **Localhost only.** The server binds to `127.0.0.1`, checks the Host and Origin headers, and requires a per-launch token.
|
|
85
|
+
- **Launch token, then cookie.** The token in the launch URL is exchanged for an `HttpOnly`, `SameSite=Strict` cookie, and the URL is cleaned up.
|
|
86
|
+
- **Strict browser policy.** Static files are served with a Content Security Policy, `nosniff`, and no-store caching. Markdown is escaped before rendering.
|
|
87
|
+
- **Reviewable releases.** `npm publish` runs lint, typecheck, i18n checks, a real-Pi offline integration test, and a tarball check. GitHub Actions publishes with npm provenance.
|
|
88
|
+
- **No telemetry.** Pi TinyChat does not send usage data anywhere.
|
|
89
|
+
|
|
90
|
+
## How it works
|
|
91
|
+
|
|
92
|
+
- **Local first.** Conversations and settings use Pi's files. UI preferences live in `localStorage`; there is no separate database or log store.
|
|
93
|
+
- **Pi-native state.** Login, models, reasoning effort, context usage, and compaction settings come from Pi.
|
|
94
|
+
- **One process per session.** Background sessions stay alive, so switching conversations does not interrupt running work.
|
|
95
|
+
- **Official RPC.** Pi runs as a separate `pi --mode rpc` process and speaks JSONL. Agent behavior stays with Pi.
|
|
96
|
+
|
|
97
|
+
## Other Pi UIs
|
|
98
|
+
|
|
99
|
+
| UI | Best for |
|
|
100
|
+
| --- | --- |
|
|
101
|
+
| **Pi TinyChat** | A small local chat with a live Plan panel and context compaction presets. No build step or runtime dependencies. |
|
|
102
|
+
| [pi-web](https://github.com/agegr/pi-web) | A full web workspace: session branching, Git worktrees, file browsing, MCP/model/skill management, subagents, and mobile. |
|
|
103
|
+
| [pi-web-simple](https://pi.dev/packages/pi-web-simple) | A simple RPC-based web UI with Git and file management. |
|
|
104
|
+
| [pipane](https://github.com/mike-heunher/pipane) | Another RPC-based browser UI for Pi. |
|
|
105
|
+
|
|
106
|
+
## Troubleshooting
|
|
107
|
+
|
|
108
|
+
- **`pi` is not found** — pass `--pi /path/to/pi` or set `PI_TINYCHAT_PI`.
|
|
109
|
+
- **The model is `unknown` or sending fails** — run `/login` in a terminal, or set a provider API key in the shell that launches Pi TinyChat. Check with `pi auth check --provider <provider>`.
|
|
110
|
+
- **Project `.pi` settings have no effect** — run `pi` once in that project and execute `/trust`. RPC mode follows Pi's trust rules.
|
|
111
|
+
- **History is empty or shows another project** — make sure `--cwd` matches the directory you use with Pi. Set `PI_CODING_AGENT_DIR` and `PI_CODING_AGENT_SESSION_DIR` in the same environment if you customize them.
|
|
112
|
+
- **The port is in use** — Pi TinyChat automatically tries the next 20 ports. Pass `--port` to choose the starting port.
|
|
113
|
+
|
|
114
|
+
## Compatibility
|
|
115
|
+
|
|
116
|
+
| Item | Detail |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| Pi version | Tested with **1.4.0**; CI also checks the latest weekly Pi release. |
|
|
119
|
+
| Node.js | **22.19** or newer. |
|
|
120
|
+
| OS | Windows 10/11, macOS, and Linux (including WSL). |
|
|
121
|
+
| Terminal-first features | `/login`, `/tree`, `/fork`, `/clone`, and TUI-only custom UI stay in the Pi terminal. |
|
|
122
|
+
| Project `.pi` | Follows Pi's trust rules. Run `/trust` once in the project before starting Pi TinyChat. |
|
|
123
|
+
| Resources | Each open session keeps a Pi process alive, so memory and CPU use grow with the number of sessions. |
|
|
124
|
+
| Multiple tabs | Session switching is not synchronized across browser tabs. |
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npm ci
|
|
130
|
+
npm link # makes the `pi-tinychat` command available locally
|
|
131
|
+
pi-tinychat # starts Pi in this repository
|
|
132
|
+
npm run check # lint, types, i18n, E2E, tarball check
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Individual checks and the manual real-model smoke test are defined in `package.json`. CI runs on Node 22.19 on Ubuntu and Windows, with a weekly check against the latest Pi.
|
|
136
|
+
|
|
137
|
+
## License
|
|
138
|
+
|
|
139
|
+
[MIT](https://github.com/VarYUvrc/pi-tinychat/blob/main/LICENSE)
|
package/bin/pi-tinychat.js
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import { resolve } from "node:path";
|
|
5
|
-
import { createTinyChat } from "../src/server.js";
|
|
6
|
-
import { locatePi } from "../src/pi-path.js";
|
|
7
|
-
|
|
8
|
-
const HELP = `Pi TinyChat — Tiny UI. Full Pi. Zero Bloat.
|
|
9
|
-
|
|
10
|
-
Usage:
|
|
11
|
-
pi-tinychat [options]
|
|
12
|
-
|
|
13
|
-
Options:
|
|
14
|
-
--cwd <path> Working directory for Pi (default: current directory)
|
|
15
|
-
--port <port> Port on 127.0.0.1 (default: $PI_TINYCHAT_PORT or 4310; uses the next free port if taken)
|
|
16
|
-
--no-open Do not open the browser automatically
|
|
17
|
-
--pi <path> Path to the pi executable or CLI entry point
|
|
18
|
-
--continue Resume the most recent Pi session in the directory
|
|
19
|
-
--help Show this help
|
|
20
|
-
--version Show the version
|
|
21
|
-
`;
|
|
22
|
-
|
|
23
|
-
function parseArgs(argv) {
|
|
24
|
-
const options = {};
|
|
25
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
26
|
-
const arg = argv[index];
|
|
27
|
-
if (arg === "--help" || arg === "-h") options.help = true;
|
|
28
|
-
else if (arg === "--version" || arg === "-v") options.version = true;
|
|
29
|
-
else if (arg === "--no-open") options.open = false;
|
|
30
|
-
else if (arg === "--continue" || arg === "-c") options.continue = true;
|
|
31
|
-
else if (arg === "--cwd") options.cwd = argv[++index];
|
|
32
|
-
else if (arg === "--port") options.port = Number(argv[++index]);
|
|
33
|
-
else if (arg === "--pi") options.pi = argv[++index];
|
|
34
|
-
else if (!arg.startsWith("-") && !options.cwd) options.cwd = arg;
|
|
35
|
-
else throw new Error(`不明なオプションです: ${arg}`);
|
|
36
|
-
}
|
|
37
|
-
return options;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function openBrowser(url) {
|
|
41
|
-
const platform = process.platform;
|
|
42
|
-
/** @type {[string, string[]]} */
|
|
43
|
-
const command =
|
|
44
|
-
platform === "win32"
|
|
45
|
-
? ["cmd", ["/c", "start", "", url]]
|
|
46
|
-
: platform === "darwin"
|
|
47
|
-
? ["open", [url]]
|
|
48
|
-
: ["xdg-open", [url]];
|
|
49
|
-
try {
|
|
50
|
-
const child = spawn(command[0], command[1], {
|
|
51
|
-
stdio: "ignore",
|
|
52
|
-
detached: true,
|
|
53
|
-
windowsHide: true,
|
|
54
|
-
});
|
|
55
|
-
child.unref();
|
|
56
|
-
} catch {
|
|
57
|
-
/* the URL is printed anyway */
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
async function main() {
|
|
62
|
-
const options = parseArgs(process.argv.slice(2));
|
|
63
|
-
if (options.help) {
|
|
64
|
-
process.stdout.write(HELP);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (options.version) {
|
|
68
|
-
const { readFile } = await import("node:fs/promises");
|
|
69
|
-
const pkg = JSON.parse(
|
|
70
|
-
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
71
|
-
);
|
|
72
|
-
process.stdout.write(`${pkg.version}\n`);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const cwd = resolve(options.cwd ?? process.cwd());
|
|
77
|
-
if (!existsSync(cwd)) throw new Error(`ディレクトリが見つかりません: ${cwd}`);
|
|
78
|
-
|
|
79
|
-
const env = { ...process.env };
|
|
80
|
-
if (options.pi) env.PI_TINYCHAT_PI = options.pi;
|
|
81
|
-
const launcher = locatePi(env);
|
|
82
|
-
const port = Number(options.port ?? process.env.PI_TINYCHAT_PORT ?? 4310);
|
|
83
|
-
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
84
|
-
throw new Error(`ポート番号が不正です: ${options.port}`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const app = await createTinyChat({
|
|
88
|
-
cwd,
|
|
89
|
-
launcher,
|
|
90
|
-
env,
|
|
91
|
-
port,
|
|
92
|
-
piArgs: options.continue ? ["--continue"] : [],
|
|
93
|
-
});
|
|
94
|
-
process.stdout.write(`\n Pi TinyChat ${app.url}\n ${cwd}\n\n`);
|
|
95
|
-
if (options.open !== false) openBrowser(app.url);
|
|
96
|
-
|
|
97
|
-
let stopping = false;
|
|
98
|
-
const shutdown = async () => {
|
|
99
|
-
if (stopping) return;
|
|
100
|
-
stopping = true;
|
|
101
|
-
await app.close();
|
|
102
|
-
process.exit(0);
|
|
103
|
-
};
|
|
104
|
-
process.on("SIGINT", shutdown);
|
|
105
|
-
process.on("SIGTERM", shutdown);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
main().catch((error) => {
|
|
109
|
-
process.stderr.write(`pi-tinychat: ${error.message}\n`);
|
|
110
|
-
process.exit(1);
|
|
111
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { createTinyChat } from "../src/server.js";
|
|
6
|
+
import { locatePi } from "../src/pi-path.js";
|
|
7
|
+
|
|
8
|
+
const HELP = `Pi TinyChat — Tiny UI. Full Pi. Zero Bloat.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
pi-tinychat [options]
|
|
12
|
+
|
|
13
|
+
Options:
|
|
14
|
+
--cwd <path> Working directory for Pi (default: current directory)
|
|
15
|
+
--port <port> Port on 127.0.0.1 (default: $PI_TINYCHAT_PORT or 4310; uses the next free port if taken)
|
|
16
|
+
--no-open Do not open the browser automatically
|
|
17
|
+
--pi <path> Path to the pi executable or CLI entry point
|
|
18
|
+
--continue Resume the most recent Pi session in the directory
|
|
19
|
+
--help Show this help
|
|
20
|
+
--version Show the version
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
function parseArgs(argv) {
|
|
24
|
+
const options = {};
|
|
25
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
26
|
+
const arg = argv[index];
|
|
27
|
+
if (arg === "--help" || arg === "-h") options.help = true;
|
|
28
|
+
else if (arg === "--version" || arg === "-v") options.version = true;
|
|
29
|
+
else if (arg === "--no-open") options.open = false;
|
|
30
|
+
else if (arg === "--continue" || arg === "-c") options.continue = true;
|
|
31
|
+
else if (arg === "--cwd") options.cwd = argv[++index];
|
|
32
|
+
else if (arg === "--port") options.port = Number(argv[++index]);
|
|
33
|
+
else if (arg === "--pi") options.pi = argv[++index];
|
|
34
|
+
else if (!arg.startsWith("-") && !options.cwd) options.cwd = arg;
|
|
35
|
+
else throw new Error(`不明なオプションです: ${arg}`);
|
|
36
|
+
}
|
|
37
|
+
return options;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function openBrowser(url) {
|
|
41
|
+
const platform = process.platform;
|
|
42
|
+
/** @type {[string, string[]]} */
|
|
43
|
+
const command =
|
|
44
|
+
platform === "win32"
|
|
45
|
+
? ["cmd", ["/c", "start", "", url]]
|
|
46
|
+
: platform === "darwin"
|
|
47
|
+
? ["open", [url]]
|
|
48
|
+
: ["xdg-open", [url]];
|
|
49
|
+
try {
|
|
50
|
+
const child = spawn(command[0], command[1], {
|
|
51
|
+
stdio: "ignore",
|
|
52
|
+
detached: true,
|
|
53
|
+
windowsHide: true,
|
|
54
|
+
});
|
|
55
|
+
child.unref();
|
|
56
|
+
} catch {
|
|
57
|
+
/* the URL is printed anyway */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function main() {
|
|
62
|
+
const options = parseArgs(process.argv.slice(2));
|
|
63
|
+
if (options.help) {
|
|
64
|
+
process.stdout.write(HELP);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (options.version) {
|
|
68
|
+
const { readFile } = await import("node:fs/promises");
|
|
69
|
+
const pkg = JSON.parse(
|
|
70
|
+
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
71
|
+
);
|
|
72
|
+
process.stdout.write(`${pkg.version}\n`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
77
|
+
if (!existsSync(cwd)) throw new Error(`ディレクトリが見つかりません: ${cwd}`);
|
|
78
|
+
|
|
79
|
+
const env = { ...process.env };
|
|
80
|
+
if (options.pi) env.PI_TINYCHAT_PI = options.pi;
|
|
81
|
+
const launcher = locatePi(env);
|
|
82
|
+
const port = Number(options.port ?? process.env.PI_TINYCHAT_PORT ?? 4310);
|
|
83
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
84
|
+
throw new Error(`ポート番号が不正です: ${options.port}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const app = await createTinyChat({
|
|
88
|
+
cwd,
|
|
89
|
+
launcher,
|
|
90
|
+
env,
|
|
91
|
+
port,
|
|
92
|
+
piArgs: options.continue ? ["--continue"] : [],
|
|
93
|
+
});
|
|
94
|
+
process.stdout.write(`\n Pi TinyChat ${app.url}\n ${cwd}\n\n`);
|
|
95
|
+
if (options.open !== false) openBrowser(app.url);
|
|
96
|
+
|
|
97
|
+
let stopping = false;
|
|
98
|
+
const shutdown = async () => {
|
|
99
|
+
if (stopping) return;
|
|
100
|
+
stopping = true;
|
|
101
|
+
await app.close();
|
|
102
|
+
process.exit(0);
|
|
103
|
+
};
|
|
104
|
+
process.on("SIGINT", shutdown);
|
|
105
|
+
process.on("SIGTERM", shutdown);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main().catch((error) => {
|
|
109
|
+
process.stderr.write(`pi-tinychat: ${error.message}\n`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
});
|