@pencere/cli 0.0.0-stage → 0.1.0
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/CHANGELOG.md +44 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.md +133 -2
- package/dist/args.d.ts +36 -0
- package/dist/args.js +139 -0
- package/dist/args.js.map +1 -0
- package/dist/connector.d.ts +44 -0
- package/dist/connector.js +90 -0
- package/dist/connector.js.map +1 -0
- package/dist/dev.d.ts +86 -0
- package/dist/dev.js +254 -0
- package/dist/dev.js.map +1 -0
- package/dist/discover.d.ts +65 -0
- package/dist/discover.js +110 -0
- package/dist/discover.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.js +21 -0
- package/dist/main.js.map +1 -0
- package/dist/run.d.ts +69 -0
- package/dist/run.js +446 -0
- package/dist/run.js.map +1 -0
- package/dist/server-process.d.ts +82 -0
- package/dist/server-process.js +322 -0
- package/dist/server-process.js.map +1 -0
- package/dist/tail.d.ts +28 -0
- package/dist/tail.js +57 -0
- package/dist/tail.js.map +1 -0
- package/dist/watch.d.ts +47 -0
- package/dist/watch.js +146 -0
- package/dist/watch.js.map +1 -0
- package/package.json +42 -4
- package/src/args.ts +158 -0
- package/src/connector.ts +107 -0
- package/src/dev.ts +303 -0
- package/src/discover.ts +137 -0
- package/src/main.ts +22 -0
- package/src/run.ts +538 -0
- package/src/server-process.ts +368 -0
- package/src/tail.ts +66 -0
- package/src/watch.ts +167 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@pencere/cli` are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the package follows
|
|
5
|
+
[semantic versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.1.0] - 2026-10-21
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `pencere run [--tool <name>] [--args <json>] [--view <dir>] -- <command> [args...]`: run an MCP
|
|
14
|
+
server that speaks stdio, written in any language, as a pencere program. The view opens in the
|
|
15
|
+
terminal pane `pencere run` runs in, and closes with ⌃C or when the view or the terminal ends
|
|
16
|
+
it. Without `--tool` the server is started once to ask which tool names a view; `--view` shows
|
|
17
|
+
the view from a directory's files, sent to the terminal once.
|
|
18
|
+
- The server runs with the program's environment, in a process group of its own so that the
|
|
19
|
+
terminal's ⌃C reaches `pencere run` and not the server, and is ended by closing its stdin, then
|
|
20
|
+
SIGTERM and SIGKILL to its group. What it wrote to stderr is shown on the screen the person gets
|
|
21
|
+
back when it failed.
|
|
22
|
+
- `pencere dev [--view <dir>] [--watch <path>]... [--tool <name>] [--args <json>] -- <command>
|
|
23
|
+
[args...]`: `pencere run`, and when the files of the view (`--view`) or the files the server reads
|
|
24
|
+
(`--watch`, a file or a directory, as often as needed) change, the view closes, the server is
|
|
25
|
+
started again and the view opens again in the same pane. Changes that come together are one
|
|
26
|
+
reopening. The terminal keeps the view's files by content, so only the files that changed cross
|
|
27
|
+
the stream. A change that leaves the files as they were, or a build that has not finished writing
|
|
28
|
+
the view, shows nothing; a terminal that cannot reopen a view is told of once, after the view has
|
|
29
|
+
closed, and the view runs as it does under `pencere run`.
|
|
30
|
+
- Exit status: 0 the view ran and closed, 1 it could not run, 2 a mistake in the command line,
|
|
31
|
+
3 the terminal does not show pencere views; ⌃C, SIGTERM and a hangup (SIGHUP, ssh going away) end
|
|
32
|
+
the view and the server, and then the process by the same signal (130, 143, 129). A terminal that
|
|
33
|
+
stopped answering while the view was shown again is a 1 with a message of its own, not a 3.
|
|
34
|
+
- A server that ends while `pencere dev` runs (a mistake saved in it) is not the end of `pencere
|
|
35
|
+
dev`: the terminal is given back, the end of what the server wrote to stderr is shown with what
|
|
36
|
+
happened, and the next change starts the server and the view again; ⌃C ends it. `pencere run`
|
|
37
|
+
still ends with the server.
|
|
38
|
+
- Which tool to show is found as the terminal would find it: a tool names a view by a `ui://`
|
|
39
|
+
resource only, and the tool list is read a page at a time up to the terminal's limit of 16 pages.
|
|
40
|
+
What a server wrote (tool names, an error message) is shown as text.
|
|
41
|
+
- A server that leaves on its own, at the end of its stdin or by falling, leaves what it started
|
|
42
|
+
behind in its group, and that is ended too (SIGTERM, then SIGKILL).
|
|
43
|
+
- A terminal that goes away (ssh dropped) fails what is written to and read from it: that is not an
|
|
44
|
+
error that ends `pencere`, which is busy letting go of the server.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
package/NOTICE
ADDED
package/README.md
CHANGED
|
@@ -1,3 +1,134 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @pencere/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run an MCP server written in any language as a pencere program, so its MCP App view opens
|
|
4
|
+
in the terminal pane you ran it in.
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
npx @pencere/cli run -- python server.py
|
|
8
|
+
npx @pencere/cli dev --view dist/view -- node dist/server.js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
[pencere](https://github.com/bateri/pencere) shows an MCP App view in the terminal pane a
|
|
12
|
+
program runs in, on your machine or on a server over plain ssh. The program is an MCP server,
|
|
13
|
+
and `@pencere/sdk` is how a TypeScript one asks the terminal for a view. `pencere run` is for
|
|
14
|
+
every other server: Python, Go, Rust, a shell script, anything that speaks MCP over stdio. It
|
|
15
|
+
runs the server as an MCP client would, talks pencere's protocol with the terminal, and carries
|
|
16
|
+
the messages between the two. The server is the one you would give Claude Desktop or VS Code,
|
|
17
|
+
unchanged. `pencere dev` is the same while you work on the view or the server: when their files
|
|
18
|
+
change, the view is shown again.
|
|
19
|
+
|
|
20
|
+
## Use
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
pencere run [options] -- <command> [args...]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Everything after `--` is the server's command, taken as it is.
|
|
27
|
+
|
|
28
|
+
| Option | What it does |
|
|
29
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `--tool <name>` | The tool whose view to show. Without it, `pencere run` starts the server once to ask (`initialize` and `tools/list`) and takes the one tool whose `_meta.ui.resourceUri` names a view. |
|
|
31
|
+
| `--args <json>` | The arguments the tool is called with, a JSON object. |
|
|
32
|
+
| `--view <dir>` | Show the view from the files in `<dir>` (it needs an `index.html`). They cross the terminal's stream once and the terminal keeps them by content. Without it the terminal reads the server's `ui://` resource. |
|
|
33
|
+
| `-h`, `--help` | Usage. |
|
|
34
|
+
| `-v`, `--version` | The version. |
|
|
35
|
+
|
|
36
|
+
The same options and exit status are `pencere dev`'s, which adds `--watch` (see below).
|
|
37
|
+
|
|
38
|
+
If the server has no tool with a view, or several, `pencere run` says which tools there are
|
|
39
|
+
and, for several, asks you to choose one with `--tool`. A tool names a view by a `ui://`
|
|
40
|
+
resource, as the terminal reads it, and the tool list is read a page at a time as far as the
|
|
41
|
+
terminal reads it (16 pages). What the server wrote is shown as text.
|
|
42
|
+
|
|
43
|
+
A terminal that does not host pencere is told so: `pencere run` says that the command is an MCP
|
|
44
|
+
server, which an MCP client can take as it is, and ends. A server is not a text interface, so it
|
|
45
|
+
is not attached to the terminal, and with `--tool` it is not started for it at all (without
|
|
46
|
+
`--tool` it was started once, to ask which tool has a view).
|
|
47
|
+
|
|
48
|
+
### Exit status
|
|
49
|
+
|
|
50
|
+
| Status | Meaning |
|
|
51
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
+
| `0` | The view ran and closed (you closed it, the view did, or the program did). |
|
|
53
|
+
| `1` | It could not run: the server did not start, did not answer as an MCP server, or ended while its view was up; the terminal refused or ended the view, or stopped answering while the view was shown again. |
|
|
54
|
+
| `2` | A mistake in the command line, or a `--view` directory that is not a view, or several tools with a view and no `--tool`. |
|
|
55
|
+
| `3` | This terminal does not show pencere views, or the input or output of `pencere run` is not a terminal. |
|
|
56
|
+
| ⌃C, `SIGTERM`, a hangup | The view and the server are ended first, then `pencere run` ends by the same signal, so the shell reads 130, 143 or 129 (the terminal went away, as when ssh does). |
|
|
57
|
+
|
|
58
|
+
When the server fails, what it wrote to stderr is shown on the screen you get back. While a
|
|
59
|
+
view is up the screen is the view's, so the end of it is kept (the last 16 KiB) and printed
|
|
60
|
+
after the view has closed, as text.
|
|
61
|
+
|
|
62
|
+
## Reloading with `pencere dev`
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
pencere dev [options] -- <command> [args...]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
pencere dev --view dist/view -- node dist/server.js # a view that is built into dist/view
|
|
70
|
+
pencere dev --watch server.py -- python server.py # a server whose own files are the work
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`pencere dev` is `pencere run`, and it watches files. When they change, the view in the pane
|
|
74
|
+
closes, the server is started again, and the view opens again, in the same pane and without
|
|
75
|
+
leaving the alternate screen. What the view held in its own memory is gone, as it is when the
|
|
76
|
+
terminal reconnects: its state is rebuilt from the server's tool results, as at the first open.
|
|
77
|
+
|
|
78
|
+
| Option | What it watches |
|
|
79
|
+
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
80
|
+
| `--view <dir>` | The view's files. A change that leaves the files as they were (a save that wrote the same bytes) shows nothing; the terminal is sent only what changed. |
|
|
81
|
+
| `--watch <path>` | A file or directory the server reads, as often as needed. A change to what it holds (size or modification time) starts the server and the view again. |
|
|
82
|
+
|
|
83
|
+
One of the two is needed. Names that begin with a dot, `node_modules` and `__pycache__` are not
|
|
84
|
+
watched under a directory: a server that writes into a directory it is watched through would end
|
|
85
|
+
every run with a change.
|
|
86
|
+
|
|
87
|
+
- **A burst is one reopening.** A build writes many files; the view is reopened once they have
|
|
88
|
+
been quiet for a moment, and a change that comes while it is reopening is shown after it.
|
|
89
|
+
- **A half-built view is not shown.** A build that has emptied its output directory and not yet
|
|
90
|
+
written `index.html` leaves something that is not a view. The change is looked at again at the
|
|
91
|
+
next one, which is the build writing more.
|
|
92
|
+
- **The terminal does the swap.** The view is closed and opened by the terminal's own messages
|
|
93
|
+
(`dismiss`; specification §10.8), which is why this works when the server runs over plain ssh
|
|
94
|
+
and the terminal is on the other side: the files cross the stream, not a port. A terminal that
|
|
95
|
+
does not list `dismiss` in its `welcome` cannot reopen a view: `pencere dev` runs the view as
|
|
96
|
+
`pencere run` would, and says after the view has closed that the changes were not shown.
|
|
97
|
+
- **What it tells you is told after.** While the view is up the screen is the view's, so notes
|
|
98
|
+
(a view that could not be read at the last change, a watch that failed) are printed on the
|
|
99
|
+
screen you get back, as the server's stderr is.
|
|
100
|
+
- **A server that ends is waited out.** If the server exits while its view is up or opening, as
|
|
101
|
+
after a syntax error is saved, the view goes with it and the terminal is yours again: `pencere dev`
|
|
102
|
+
says that the server ended, shows the end of what it wrote to stderr, and waits. The next change
|
|
103
|
+
to the files it watches starts the server and the view again, and nothing has to be run twice.
|
|
104
|
+
A change that leaves the files as they were is not another try. ⌃C ends `pencere dev` as ever.
|
|
105
|
+
`pencere run`, which watches nothing, ends with the server.
|
|
106
|
+
|
|
107
|
+
## How the server is run
|
|
108
|
+
|
|
109
|
+
- **As an MCP client runs it.** The command is started with your whole environment and working
|
|
110
|
+
directory, with pipes for stdin and stdout; stdout carries one JSON-RPC message to a line.
|
|
111
|
+
Each view is a new MCP session and so a new process: a stdio server expects one `initialize`
|
|
112
|
+
for the life of its process, and a view that the terminal starts again (after a reconnection)
|
|
113
|
+
or that the program reopens gets a server of its own.
|
|
114
|
+
- **In a process group of its own.** ⌃C is the terminal's signal to the foreground group, which
|
|
115
|
+
is `pencere run`'s. The server must not take it: `pencere run` ends it, after telling the
|
|
116
|
+
terminal the program is done. It closes the server's stdin first, which every stdio server
|
|
117
|
+
takes as the end, then sends SIGTERM and SIGKILL to the whole group, which catches what a
|
|
118
|
+
wrapper (`npx`, `uv run`, `sh -c`) started. A server that leaves at the end of its stdin, or
|
|
119
|
+
falls, leaves what it started behind in the group, and that gets the same two signals.
|
|
120
|
+
- **Nothing else is added.** What the terminal asks (the question, the token, the bundle's
|
|
121
|
+
files, ⌃C and `bye`, starting the view again) is done by `@pencere/sdk`'s `serve`, which this
|
|
122
|
+
package runs; `pencere run` does not parse the server's messages.
|
|
123
|
+
|
|
124
|
+
Node 22 or later is needed, on a system with `stty` (macOS, Linux): the SDK sets the terminal's
|
|
125
|
+
modes with it, so Windows has no host as far as it is concerned.
|
|
126
|
+
|
|
127
|
+
## Versions
|
|
128
|
+
|
|
129
|
+
This release speaks version 1 of the pencere wire (a draft), through `@pencere/sdk`. The package
|
|
130
|
+
follows semantic versioning; [CHANGELOG.md](CHANGELOG.md) says what each release changed.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
Apache-2.0 — see [LICENSE](LICENSE).
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command line: `pencere run [options] -- <command> [args...]` and
|
|
3
|
+
* `pencere dev [options] -- <command> [args...]`.
|
|
4
|
+
*
|
|
5
|
+
* Everything after the first `--` is the server's command, taken as it is: a
|
|
6
|
+
* server's own flags are not pencere's to read. What comes before it is read with
|
|
7
|
+
* `node:util`'s `parseArgs`, strictly, so a mistyped option is an error and not a
|
|
8
|
+
* flag handed to nobody.
|
|
9
|
+
*/
|
|
10
|
+
export declare const USAGE = "usage: pencere run [options] -- <command> [args...]\n pencere dev [options] -- <command> [args...]\n\nrun: runs <command>, an MCP server that speaks stdio and is written in any\nlanguage, and shows its view in this terminal pane. The server is started as an\nMCP client would start it, so it is the same server an MCP Apps host runs.\n\n pencere run -- python server.py\n pencere run --tool list_containers --view ./view -- node dist/server.js\n\ndev: the same, and the view is shown again when its files change (--view) or\nwhen the server's own files do (--watch): the view closes, the server is\nstarted again and the view opens again, with only the files that changed sent.\n\n pencere dev --view dist/view -- node dist/server.js\n pencere dev --watch server.py -- python server.py\n\nA server that ends while dev runs (a mistake saved in it) is told of, with the end\nof what it wrote to stderr, and tried again at the next change; ^C ends dev.\n\noptions:\n --tool <name> the tool whose view to show; without it, the one tool of the\n server that names a view (_meta.ui.resourceUri)\n --args <json> the arguments the tool is called with, a JSON object\n --view <dir> show the view from the files in <dir> (sent to the terminal\n once, then kept by content) instead of the server's own\n ui:// resource\n --watch <path> dev only, repeatable: a file or directory whose change starts\n the server and the view again; --view's directory is watched\n already, and one of --view and --watch is needed\n -h, --help this text\n -v, --version the version of pencere\n\nexit status: 0 the view ran and closed; 1 it could not run (the server failed to\nstart or ended, the terminal refused or ended the view, or stopped answering\nwhile the view was shown again); 2 a mistake in the command line; 3 this terminal\ndoes not show pencere views; 130, 143 and 129 after ^C, SIGTERM and a hangup.\n";
|
|
11
|
+
/** The server to run and the view to show, which `run` and `dev` are both given. */
|
|
12
|
+
export interface Launch {
|
|
13
|
+
readonly command: string;
|
|
14
|
+
readonly args: readonly string[];
|
|
15
|
+
readonly tool?: string;
|
|
16
|
+
readonly arguments?: Record<string, unknown>;
|
|
17
|
+
readonly view?: string;
|
|
18
|
+
}
|
|
19
|
+
/** What the command line asks for. */
|
|
20
|
+
export type Command = {
|
|
21
|
+
readonly kind: "help";
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: "version";
|
|
24
|
+
} | ({
|
|
25
|
+
readonly kind: "run";
|
|
26
|
+
} & Launch) | ({
|
|
27
|
+
readonly kind: "dev";
|
|
28
|
+
/** The files whose change starts the server and the view again, besides the view's directory. */
|
|
29
|
+
readonly watch: readonly string[];
|
|
30
|
+
} & Launch);
|
|
31
|
+
/** A mistake in the command line: what it is, so that the person can fix it. */
|
|
32
|
+
export declare class UsageError extends Error {
|
|
33
|
+
constructor(message: string);
|
|
34
|
+
}
|
|
35
|
+
/** @throws {UsageError} for anything that is not a command of pencere. */
|
|
36
|
+
export declare function parse(argv: readonly string[]): Command;
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The command line: `pencere run [options] -- <command> [args...]` and
|
|
3
|
+
* `pencere dev [options] -- <command> [args...]`.
|
|
4
|
+
*
|
|
5
|
+
* Everything after the first `--` is the server's command, taken as it is: a
|
|
6
|
+
* server's own flags are not pencere's to read. What comes before it is read with
|
|
7
|
+
* `node:util`'s `parseArgs`, strictly, so a mistyped option is an error and not a
|
|
8
|
+
* flag handed to nobody.
|
|
9
|
+
*/
|
|
10
|
+
import { parseArgs } from "node:util";
|
|
11
|
+
export const USAGE = `\
|
|
12
|
+
usage: pencere run [options] -- <command> [args...]
|
|
13
|
+
pencere dev [options] -- <command> [args...]
|
|
14
|
+
|
|
15
|
+
run: runs <command>, an MCP server that speaks stdio and is written in any
|
|
16
|
+
language, and shows its view in this terminal pane. The server is started as an
|
|
17
|
+
MCP client would start it, so it is the same server an MCP Apps host runs.
|
|
18
|
+
|
|
19
|
+
pencere run -- python server.py
|
|
20
|
+
pencere run --tool list_containers --view ./view -- node dist/server.js
|
|
21
|
+
|
|
22
|
+
dev: the same, and the view is shown again when its files change (--view) or
|
|
23
|
+
when the server's own files do (--watch): the view closes, the server is
|
|
24
|
+
started again and the view opens again, with only the files that changed sent.
|
|
25
|
+
|
|
26
|
+
pencere dev --view dist/view -- node dist/server.js
|
|
27
|
+
pencere dev --watch server.py -- python server.py
|
|
28
|
+
|
|
29
|
+
A server that ends while dev runs (a mistake saved in it) is told of, with the end
|
|
30
|
+
of what it wrote to stderr, and tried again at the next change; ^C ends dev.
|
|
31
|
+
|
|
32
|
+
options:
|
|
33
|
+
--tool <name> the tool whose view to show; without it, the one tool of the
|
|
34
|
+
server that names a view (_meta.ui.resourceUri)
|
|
35
|
+
--args <json> the arguments the tool is called with, a JSON object
|
|
36
|
+
--view <dir> show the view from the files in <dir> (sent to the terminal
|
|
37
|
+
once, then kept by content) instead of the server's own
|
|
38
|
+
ui:// resource
|
|
39
|
+
--watch <path> dev only, repeatable: a file or directory whose change starts
|
|
40
|
+
the server and the view again; --view's directory is watched
|
|
41
|
+
already, and one of --view and --watch is needed
|
|
42
|
+
-h, --help this text
|
|
43
|
+
-v, --version the version of pencere
|
|
44
|
+
|
|
45
|
+
exit status: 0 the view ran and closed; 1 it could not run (the server failed to
|
|
46
|
+
start or ended, the terminal refused or ended the view, or stopped answering
|
|
47
|
+
while the view was shown again); 2 a mistake in the command line; 3 this terminal
|
|
48
|
+
does not show pencere views; 130, 143 and 129 after ^C, SIGTERM and a hangup.
|
|
49
|
+
`;
|
|
50
|
+
/** A mistake in the command line: what it is, so that the person can fix it. */
|
|
51
|
+
export class UsageError extends Error {
|
|
52
|
+
constructor(message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.name = "UsageError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** The arguments of a tool call: one JSON object. */
|
|
58
|
+
function toolArguments(text) {
|
|
59
|
+
let value;
|
|
60
|
+
try {
|
|
61
|
+
value = JSON.parse(text);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
throw new UsageError(`--args is not JSON: ${error instanceof Error ? error.message : error}`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
67
|
+
throw new UsageError("--args must be a JSON object");
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
const COMMANDS = "use `pencere run [options] -- <command> [args...]` or `pencere dev ...`";
|
|
71
|
+
/** @throws {UsageError} for anything that is not a command of pencere. */
|
|
72
|
+
export function parse(argv) {
|
|
73
|
+
const [name, ...rest] = argv;
|
|
74
|
+
if (name === undefined)
|
|
75
|
+
throw new UsageError(`no command: ${COMMANDS}`);
|
|
76
|
+
if (name === "-h" || name === "--help" || name === "help")
|
|
77
|
+
return { kind: "help" };
|
|
78
|
+
if (name === "-v" || name === "--version")
|
|
79
|
+
return { kind: "version" };
|
|
80
|
+
if (name !== "run" && name !== "dev")
|
|
81
|
+
throw new UsageError(`unknown command "${name}": ${COMMANDS}`);
|
|
82
|
+
// The server's command is everything after the first `--`.
|
|
83
|
+
const dash = rest.indexOf("--");
|
|
84
|
+
const own = dash === -1 ? rest : rest.slice(0, dash);
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = parseOwn(own);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw new UsageError(error instanceof Error ? error.message : String(error));
|
|
91
|
+
}
|
|
92
|
+
const { values, positionals } = parsed;
|
|
93
|
+
if (values.help === true)
|
|
94
|
+
return { kind: "help" };
|
|
95
|
+
const [stray] = positionals;
|
|
96
|
+
if (stray !== undefined)
|
|
97
|
+
throw new UsageError(`unexpected "${stray}": the server's command goes after --`);
|
|
98
|
+
if (dash === -1)
|
|
99
|
+
throw new UsageError("no command: put the server's command after --");
|
|
100
|
+
const [command, ...args] = rest.slice(dash + 1);
|
|
101
|
+
if (command === undefined || command === "")
|
|
102
|
+
throw new UsageError("no command after --");
|
|
103
|
+
for (const option of ["tool", "view"])
|
|
104
|
+
if (values[option] === "")
|
|
105
|
+
throw new UsageError(`--${option} needs a value`);
|
|
106
|
+
const watch = values.watch ?? [];
|
|
107
|
+
if (watch.includes(""))
|
|
108
|
+
throw new UsageError("--watch needs a value");
|
|
109
|
+
const launch = {
|
|
110
|
+
command,
|
|
111
|
+
args,
|
|
112
|
+
...(values.tool === undefined ? {} : { tool: values.tool }),
|
|
113
|
+
...(values.args === undefined ? {} : { arguments: toolArguments(values.args) }),
|
|
114
|
+
...(values.view === undefined ? {} : { view: values.view }),
|
|
115
|
+
};
|
|
116
|
+
if (name === "run") {
|
|
117
|
+
if (values.watch !== undefined)
|
|
118
|
+
throw new UsageError("--watch is for `pencere dev`: `pencere run` shows the view once");
|
|
119
|
+
return { kind: "run", ...launch };
|
|
120
|
+
}
|
|
121
|
+
if (launch.view === undefined && watch.length === 0)
|
|
122
|
+
throw new UsageError("nothing to watch: give --view <dir> for the view's files, or --watch <path> for the server's own");
|
|
123
|
+
return { kind: "dev", watch, ...launch };
|
|
124
|
+
}
|
|
125
|
+
function parseOwn(args) {
|
|
126
|
+
return parseArgs({
|
|
127
|
+
args: [...args],
|
|
128
|
+
options: {
|
|
129
|
+
tool: { type: "string" },
|
|
130
|
+
args: { type: "string" },
|
|
131
|
+
view: { type: "string" },
|
|
132
|
+
watch: { type: "string", multiple: true },
|
|
133
|
+
help: { type: "boolean", short: "h" },
|
|
134
|
+
},
|
|
135
|
+
allowPositionals: true,
|
|
136
|
+
strict: true,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The program's MCP server, stood in for by a process.
|
|
3
|
+
*
|
|
4
|
+
* `serve` of the SDK takes anything that can `connect` a transport and `close`
|
|
5
|
+
* the session, and does everything else of a pencere program: the question to the
|
|
6
|
+
* terminal, the token, the bundle, ⌃C and `bye`, the host starting the view again
|
|
7
|
+
* (§10.7) and the program reopening it (§10.8). It connects once for each view it
|
|
8
|
+
* opens. This is that server for a program that has none of its own: it starts the
|
|
9
|
+
* command for each connection and carries the JSON-RPC messages between the
|
|
10
|
+
* terminal's stream and the command's stdio, line by line, untouched. A server
|
|
11
|
+
* that has been connected to a view once has done its `initialize`; the next view
|
|
12
|
+
* is a new session, and so a new process, which is also what a stdio server
|
|
13
|
+
* expects: one `initialize` for the life of the process.
|
|
14
|
+
*/
|
|
15
|
+
import type { Transport } from "@modelcontextprotocol/server";
|
|
16
|
+
import type { Connectable } from "@pencere/sdk";
|
|
17
|
+
import type { CloseTiming, Exit, ServerCommand } from "./server-process.js";
|
|
18
|
+
import type { Tail } from "./tail.js";
|
|
19
|
+
export interface ConnectorOptions {
|
|
20
|
+
/** What the server writes to stderr goes here, as do the connector's own notes on the stream. */
|
|
21
|
+
readonly tail: Tail;
|
|
22
|
+
/**
|
|
23
|
+
* The server's process ended, and the connector had not asked it to. The view
|
|
24
|
+
* has nothing to talk to any more: the caller ends it.
|
|
25
|
+
*/
|
|
26
|
+
readonly unexpectedExit: (exit: Exit) => void;
|
|
27
|
+
readonly timing?: CloseTiming;
|
|
28
|
+
}
|
|
29
|
+
export declare class ProcessConnector implements Connectable {
|
|
30
|
+
#private;
|
|
31
|
+
constructor(spec: ServerCommand, options: ConnectorOptions);
|
|
32
|
+
/** How many processes this connector has started: one for each view that was opened. */
|
|
33
|
+
get starts(): number;
|
|
34
|
+
/** @throws {StartError} when the command cannot be started. */
|
|
35
|
+
connect(transport: Transport): Promise<void>;
|
|
36
|
+
/** Ends the process of the session that is connected, if there is one, and resolves when it is gone. */
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolves when every process this connector started has ended. `serve` lets go of
|
|
40
|
+
* a session without waiting for it (a signal must not wait for a stubborn server),
|
|
41
|
+
* and a program that is about to leave has to.
|
|
42
|
+
*/
|
|
43
|
+
drain(): Promise<void>;
|
|
44
|
+
}
|