dsh-penpot-plugin 0.0.0-stage → 0.3.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/CHANGELOG.md +85 -0
- package/COMPATIBILITY.md +20 -0
- package/LICENSE +21 -0
- package/README.md +124 -2
- package/cordis.patch.yml +9 -0
- package/docs/DESIGN.md +53 -0
- package/docs/STANDALONE.md +76 -0
- package/lib/api-backend.d.ts +27 -0
- package/lib/api-backend.js +172 -0
- package/lib/api-backend.js.map +1 -0
- package/lib/backend.d.ts +51 -0
- package/lib/backend.js +15 -0
- package/lib/backend.js.map +1 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +291 -0
- package/lib/index.js.map +1 -0
- package/package.json +61 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1
|
|
4
|
+
|
|
5
|
+
- **Reads ask for JSON.** Penpot answers transit unless the request sends
|
|
6
|
+
`Accept: application/json`; without it every list came back as transit and
|
|
7
|
+
parsed into empty refs (`penpot_teams` showed `{"id":"","name":""}`). Every
|
|
8
|
+
RPC request now sends the header.
|
|
9
|
+
- **`penpot_export` reads Penpot's real response.** `export-binfile` is an SSE
|
|
10
|
+
stream that ends with the URL of the exported zip; the plugin treated the
|
|
11
|
+
stream itself as the file and wrote `event: progress` text as a `.penpot`.
|
|
12
|
+
It now parses the stream, downloads the URL and returns the zip bytes.
|
|
13
|
+
- **`penpot_import` reads Penpot's real response.** `import-binfile` is also
|
|
14
|
+
SSE and ends with the id(s) of the imported file(s); the plugin called
|
|
15
|
+
`.json()` and failed after a successful import. It now parses the stream and
|
|
16
|
+
returns the file id.
|
|
17
|
+
- **`penpot_search` sends the team.** Penpot's `search-files` requires a
|
|
18
|
+
`team-id`; the tool takes an optional `teamId` and otherwise searches the
|
|
19
|
+
first team the account can see.
|
|
20
|
+
- SSE event data is transit-encoded (`~:` keys, `~u` uuids); a small normalizer
|
|
21
|
+
handles both markers so errors and ids are readable without a transit
|
|
22
|
+
dependency.
|
|
23
|
+
|
|
24
|
+
## 0.3.0
|
|
25
|
+
|
|
26
|
+
- **npm distribution.** The package is published as
|
|
27
|
+
`dsh-penpot-plugin@<version>`; the registry tarball contains `lib/`, so
|
|
28
|
+
installs no longer run a build script. The release workflow publishes a tag
|
|
29
|
+
with provenance. The setup pins this npm spec and keeps the GitHub tag as a
|
|
30
|
+
fallback.
|
|
31
|
+
- **`penpot_import`.** The export counterpart is now a tool: upload a `.penpot`
|
|
32
|
+
file as a new file (`projectId`) or replace an existing one (`fileId`). The
|
|
33
|
+
backend already had the multipart `import-binfile` call; the setup CLI was the
|
|
34
|
+
only caller.
|
|
35
|
+
- **Bundle patch corrected.** `cordis.patch.yml` still passed `backend: auto`
|
|
36
|
+
and `mcpServer`, which 0.2.0 removed. The row now declares only `apiUrl` and
|
|
37
|
+
`token`, so a standalone install no longer starts with a stale config key.
|
|
38
|
+
- **Builds clean before packaging.** `lib/` is removed before every build, so
|
|
39
|
+
compiled files from removed modules (the old MCP backend) can never enter a
|
|
40
|
+
tarball.
|
|
41
|
+
- **The package description no longer claims an MCP backend.** It was stale
|
|
42
|
+
since 0.2.0 and is the text GitHub and npm show.
|
|
43
|
+
- **Standalone is documented as a first-class path** (`docs/STANDALONE.md`): no
|
|
44
|
+
setup, no managed row, same tools. The setup remains the convenience wrapper,
|
|
45
|
+
not a requirement.
|
|
46
|
+
- **Load fix.** The plugin used `ctx.tools` without declaring
|
|
47
|
+
`inject = ['tools']`; Cordis refuses that property, so a standalone profile
|
|
48
|
+
failed at load with `cannot get property "tools" without inject`. The setup
|
|
49
|
+
masked it only while the plugin happened not to be mounted. Declared now.
|
|
50
|
+
- Tool-surface tests cover the service declaration, the always-registered set,
|
|
51
|
+
the unconfigured status, and the import destination check.
|
|
52
|
+
|
|
53
|
+
## 0.2.0
|
|
54
|
+
|
|
55
|
+
- **MCP backend removed.** The plugin talks to Penpot's local HTTP RPC only.
|
|
56
|
+
`backend`, `mcpServer` and the `mcp__<server>__*` adapter are gone; the
|
|
57
|
+
read-only MCP path could not export and could silently replace the API via
|
|
58
|
+
`auto`.
|
|
59
|
+
- The plugin now loads even when `PENPOT_API_URL`/`PENPOT_ACCESS_TOKEN` are
|
|
60
|
+
empty: `penpot_status` reports `none`, the other tools throw one readable
|
|
61
|
+
configuration error.
|
|
62
|
+
- README/DESIGN updated for the systemd/Podman setup (`--design penpot`).
|
|
63
|
+
|
|
64
|
+
## 0.1.2
|
|
65
|
+
|
|
66
|
+
Docs only. The setup path now says `/system components` (the command was renamed
|
|
67
|
+
from `/system manage`).
|
|
68
|
+
|
|
69
|
+
## 0.1.1
|
|
70
|
+
|
|
71
|
+
- Docs only. The install section now leads with the DSH setup's design
|
|
72
|
+
component (`install.sh --design docker` / `/system manage`), pins the manual
|
|
73
|
+
install at a tag, and explains the pnpm `allowBuilds` gate that otherwise
|
|
74
|
+
aborts a git install. Credentials are described as the runtime process's
|
|
75
|
+
environment, not a shell export.
|
|
76
|
+
- `COMPATIBILITY.md` records that the setup pins this plugin and the Penpot
|
|
77
|
+
images, so a ref or image bump is a setup release.
|
|
78
|
+
|
|
79
|
+
## 0.1.0
|
|
80
|
+
|
|
81
|
+
- First cut: `penpot_status`, `penpot_teams`, `penpot_projects`,
|
|
82
|
+
`penpot_files`, `penpot_search`, `penpot_file`, `penpot_export`.
|
|
83
|
+
- Two backends behind one interface: the HTTP API with an access token, and an
|
|
84
|
+
MCP server already registered in the runtime. `auto` prefers MCP read tools.
|
|
85
|
+
- Export writes a `.penpot` file; import is not exposed as a tool yet.
|
package/COMPATIBILITY.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Compatibility
|
|
2
|
+
|
|
3
|
+
The plugin talks to Penpot, not to DSH internals, so its DSH requirement is only
|
|
4
|
+
the peer range of the tool registry. Penpot's RPC is internal and moves between
|
|
5
|
+
releases; every row states what was actually exercised.
|
|
6
|
+
|
|
7
|
+
| Plugin | DSH | Penpot | Status | Notes |
|
|
8
|
+
|---|---|---|---|---|
|
|
9
|
+
| 0.3.1 | `>=0.1.0-rc.6 <0.3.0-0` | 2.18.6 | live instance: teams/projects/search/import/export exercised | fixes the JSON accept header, the SSE export/import responses and the required `team-id` on search |
|
|
10
|
+
| 0.3.0 | `>=0.1.0-rc.6 <0.3.0-0` | 2.18.6 | API import covered by a fake server; live instance run is the release gate | adds `penpot_import`; bundle patch drops the removed `backend`/`mcpServer` keys; standalone install documented |
|
|
11
|
+
| 0.2.0 | `>=0.1.0-rc.6 <0.3.0-0` | 2.18.6 | API exercised against a live self-hosted instance | API only: the optional MCP backend and `backend`/`mcpServer` config were removed; the plugin now loads unconfigured and `penpot_status` says `none` |
|
|
12
|
+
| 0.1.2 | `>=0.1.0-rc.6 <0.3.0-0` | 2.18 | untested against a live instance | docs only: `/system components` after the setup rename; same code as 0.1.1 |
|
|
13
|
+
| 0.1.1 | `>=0.1.0-rc.6 <0.3.0-0` | 2.18 | untested against a live instance | docs only: the setup path is documented as primary, the manual install is pinned and the pnpm build gate is explained; same code as 0.1.0 |
|
|
14
|
+
| 0.1.0 | `>=0.1.0-rc.6 <0.3.0-0` | 2.18 | untested against a live instance | read tools + export; request shapes checked against Penpot's OpenAPI and a fake server, not a real deployment |
|
|
15
|
+
|
|
16
|
+
The setup's design component is the reference deployment: `plugins.json` pins
|
|
17
|
+
this plugin at `v0.3.0` and `components.json` pins the Penpot images at `2.18`.
|
|
18
|
+
The plugin version is independent of the setup release; the setup pin is what a
|
|
19
|
+
fresh deployment installs, and a newer compatible tag installed from the bot
|
|
20
|
+
stays installed across reboots.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vladtsvirko
|
|
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,3 +1,125 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-penpot-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Penpot for [DeepSeek Harness](https://github.com/deepseek-ai): list, search,
|
|
4
|
+
inspect and export design files from a DSH agent over Penpot's local HTTP API.
|
|
5
|
+
|
|
6
|
+
`0.2` is deliberately read + export. Writing designs needs Penpot's
|
|
7
|
+
`update-file` change format; the design questions are in
|
|
8
|
+
[docs/DESIGN.md](docs/DESIGN.md).
|
|
9
|
+
|
|
10
|
+
There is no MCP backend. The setup runs a local Penpot instance and talks to it
|
|
11
|
+
over `/api/rpc/command/*` with an access token; a second transport added a
|
|
12
|
+
read-only path that could not export, so it was removed in 0.2.0.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
### In the DSH setup (recommended)
|
|
17
|
+
|
|
18
|
+
The setup's optional design component installs Penpot, the `penpot` CLI and this
|
|
19
|
+
plugin together, with the plugin ref pinned in `components.json`:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
./install.sh --design penpot # or add it later: /system components -> Enable
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That path is self-contained: it writes `PENPOT_API_URL` and
|
|
26
|
+
`PENPOT_ACCESS_TOKEN` into the runtime environment, installs the pinned plugin
|
|
27
|
+
and mounts a managed `dsh-penpot` row. Nothing below is needed.
|
|
28
|
+
|
|
29
|
+
### Standalone (any DSH runtime)
|
|
30
|
+
|
|
31
|
+
See [docs/STANDALONE.md](docs/STANDALONE.md) for the complete walkthrough.
|
|
32
|
+
|
|
33
|
+
The plugin is a normal DSH bundle: it works in any profile, with or without the
|
|
34
|
+
setup. It needs a Penpot instance and an access token, nothing else from this
|
|
35
|
+
project. It is published to npm as an exact version:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
dsh plugin --profile web add dsh-penpot-plugin@0.3.0
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The registry tarball contains the built `lib/`, so there is no `prepare` build
|
|
42
|
+
and no `allowBuilds` entry. Restart the runtime after adding it.
|
|
43
|
+
|
|
44
|
+
From the source repository instead, allow the build and pin the tag:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
allowBuilds:
|
|
48
|
+
dsh-penpot-plugin@https://github.com/vladtsvirko/dsh-penpot-plugin.git: true
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
dsh plugin --profile web add github:vladtsvirko/dsh-penpot-plugin#v0.3.0
|
|
53
|
+
```
|
|
54
|
+
The row reads its credentials from the
|
|
55
|
+
environment of the **runtime process** — exporting them in a shell later does
|
|
56
|
+
not reach a running runtime:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
export PENPOT_API_URL='http://192.168.2.50:8090' # no /api/rpc suffix needed
|
|
60
|
+
export PENPOT_ACCESS_TOKEN='...' # Penpot: Settings -> Access tokens
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
In the DSH setup that means adding both to `.env` and restarting
|
|
64
|
+
`dsh.service`; the design component does it for you. Ask the agent for
|
|
65
|
+
`penpot_status` to verify: it names the endpoint, and a missing variable is
|
|
66
|
+
reported by name.
|
|
67
|
+
|
|
68
|
+
## Tools
|
|
69
|
+
|
|
70
|
+
| Tool | What it does |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `penpot_status` | whether the API is configured, and its endpoint |
|
|
73
|
+
| `penpot_teams` | teams the account can see |
|
|
74
|
+
| `penpot_projects` | projects, optionally per team |
|
|
75
|
+
| `penpot_files` | files in a project |
|
|
76
|
+
| `penpot_search` | files by name or term |
|
|
77
|
+
| `penpot_file` | one file: id, name, pages |
|
|
78
|
+
| `penpot_export` | write a `.penpot` file to disk |
|
|
79
|
+
| `penpot_import` | upload a `.penpot` file as a new or replacement file |
|
|
80
|
+
|
|
81
|
+
Export gives a zip of Penpot JSON: unzip it, read or edit with the normal file
|
|
82
|
+
tools, then import it back with `penpot_import` — no setup CLI and no second
|
|
83
|
+
toolchain. A new file needs `projectId`; replacing one needs `fileId`.
|
|
84
|
+
|
|
85
|
+
## Backend
|
|
86
|
+
|
|
87
|
+
One backend: Penpot's HTTP RPC.
|
|
88
|
+
|
|
89
|
+
- `POST /api/rpc/command/<name>` with `Authorization: Token …`, kebab-case
|
|
90
|
+
payloads (`get-teams`, `get-all-projects`, `get-files`, `search-files`,
|
|
91
|
+
`get-file-summary`, `export-binfile`, `import-binfile`).
|
|
92
|
+
- Configuration is `apiUrl` + `token`; both come from the environment in the
|
|
93
|
+
setup's managed row. If either is empty the plugin still loads, and
|
|
94
|
+
`penpot_status` says `none` while the other tools explain what to set.
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
- id: dsh-penpot
|
|
98
|
+
config:
|
|
99
|
+
apiUrl: http://192.168.2.50:8090
|
|
100
|
+
token: !!js process.env.PENPOT_ACCESS_TOKEN ?? ''
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Updating
|
|
104
|
+
|
|
105
|
+
Plugin versions are independent of DSH and of the setup release.
|
|
106
|
+
|
|
107
|
+
- **In the setup:** `/system update penpot` in the bot (or `/system update
|
|
108
|
+
plugin dsh-penpot-plugin`) resolves the newest version, opens a rollback trial,
|
|
109
|
+
and replaces the bundle through the host's plugin manager.
|
|
110
|
+
- **Standalone:** `dsh plugin --profile <name> remove dsh-penpot-plugin`, then
|
|
111
|
+
`add` the new tag. The plugin manager cannot install over an already-installed
|
|
112
|
+
git package, so it is two steps; restart the runtime when it reports
|
|
113
|
+
`restart-required`.
|
|
114
|
+
|
|
115
|
+
The `dsh-penpot` row's configuration is preserved across an update; only the
|
|
116
|
+
package moves.
|
|
117
|
+
|
|
118
|
+
## Compatibility
|
|
119
|
+
|
|
120
|
+
Plugin versions are independent of DSH and of the setup release. Each release
|
|
121
|
+
states the DSH range and the Penpot versions it was tested against in
|
|
122
|
+
[COMPATIBILITY.md](COMPATIBILITY.md). The setup pins an exact npm spec in
|
|
123
|
+
`plugins.json`; a newer compatible version installed from the bot stays
|
|
124
|
+
installed across reboots, and the GitHub tag of the same release is the
|
|
125
|
+
registry-unreachable fallback.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
- insert:
|
|
2
|
+
- id: dsh-penpot
|
|
3
|
+
name: dsh-penpot-plugin
|
|
4
|
+
config:
|
|
5
|
+
# Both are required for a working backend; the environment is the safe
|
|
6
|
+
# place for them. An empty pair is supported: the plugin still loads and
|
|
7
|
+
# penpot_status reports that it is not configured.
|
|
8
|
+
apiUrl: !!js process.env.PENPOT_API_URL ?? ""
|
|
9
|
+
token: !!js process.env.PENPOT_ACCESS_TOKEN ?? ""
|
package/docs/DESIGN.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Design
|
|
2
|
+
|
|
3
|
+
## Shape
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
tools (defineTool) ──► PenpotBackend ──► api: Penpot RPC + access token
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
One backend, one set of model-facing names. There is no MCP path: the plugin
|
|
10
|
+
runs beside a local Penpot instance and uses its HTTP RPC, which is the same
|
|
11
|
+
surface the `penpot` CLI uses.
|
|
12
|
+
|
|
13
|
+
## Backend
|
|
14
|
+
|
|
15
|
+
`POST /api/rpc/command/<name>` with `Authorization: Token …`, kebab-case
|
|
16
|
+
payloads. Ported from the `penpot` CLI in the setup repo.
|
|
17
|
+
|
|
18
|
+
- `get-teams`, `get-all-projects`/`get-projects`, `get-files`,
|
|
19
|
+
`search-files`, `get-file-summary`, `export-binfile`, `import-binfile`.
|
|
20
|
+
- A missing `PENPOT_API_URL` or `PENPOT_ACCESS_TOKEN` leaves the plugin loaded
|
|
21
|
+
but unconfigured: `penpot_status` reports `none`, every other tool throws the
|
|
22
|
+
same readable error instead of failing the whole plugin at load.
|
|
23
|
+
|
|
24
|
+
## Why no MCP
|
|
25
|
+
|
|
26
|
+
MCP was an optional second transport in 0.1. It delegated to tools registered by
|
|
27
|
+
`@deepseek-ai/dsh-mcp-client`, so it needed a separate MCP server, exposed only
|
|
28
|
+
read operations, and refused export/import. With the direct API available, it
|
|
29
|
+
added a configuration footgun (`backend: auto` would pick the read-only MCP and
|
|
30
|
+
break `penpot_export`) and no capability. Removed in 0.2.0.
|
|
31
|
+
|
|
32
|
+
The hosted `penpot.app` MCP is a remote service for Penpot's cloud account; it is
|
|
33
|
+
not used by this local setup, and routing designs through it would leave the
|
|
34
|
+
host.
|
|
35
|
+
|
|
36
|
+
## Open questions (v0.2+)
|
|
37
|
+
|
|
38
|
+
1. **Writes.** Penpot's `update-file` takes a `changes` array (`add-obj`,
|
|
39
|
+
`mod-obj`, `del-obj`) against its shape model. The pragmatic path is
|
|
40
|
+
shape-by-shape: text first, then fill, then geometry, each with a Penpot
|
|
41
|
+
version row in the matrix.
|
|
42
|
+
2. **Import.** Implemented in 0.3.0 as `penpot_import` (multipart
|
|
43
|
+
`import-binfile`). The shape is covered by a fake server; a real instance
|
|
44
|
+
run is still the release gate for the compatibility row.
|
|
45
|
+
3. **Resources.** A file could be exposed as a DSH resource rather than fetched
|
|
46
|
+
through a tool; depends on what DSH's resource surface wants.
|
|
47
|
+
|
|
48
|
+
## Non-goals
|
|
49
|
+
|
|
50
|
+
- The live canvas. That is the browser plugin API, reachable only through a
|
|
51
|
+
human tab, not a headless plugin.
|
|
52
|
+
- Owning Penpot's data model. Everything goes through the API; no direct
|
|
53
|
+
Postgres reads.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Standalone use
|
|
2
|
+
|
|
3
|
+
`dsh-penpot-plugin` is a normal DSH bundle. The setup's design component is a
|
|
4
|
+
convenience: it installs Penpot, the `penpot` CLI and this plugin, and wires the
|
|
5
|
+
environment. None of that is required by the plugin.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
| What | Why |
|
|
10
|
+
|---|---|
|
|
11
|
+
| DSH matching the range in [COMPATIBILITY.md](../COMPATIBILITY.md) | the tool registry API |
|
|
12
|
+
| A reachable Penpot instance (2.x) | the HTTP RPC backend |
|
|
13
|
+
| A Penpot access token | Settings → Access tokens |
|
|
14
|
+
| Node 22 and pnpm | `dsh plugin`; source installs also need the build script |
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
# Published package: the tarball already contains lib/, so no build script.
|
|
20
|
+
dsh plugin --profile penpot add dsh-penpot-plugin@0.3.0
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
From the source repository instead, allow the build and pin the tag:
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
allowBuilds:
|
|
27
|
+
dsh-penpot-plugin@https://github.com/vladtsvirko/dsh-penpot-plugin.git: true
|
|
28
|
+
```
|
|
29
|
+
```sh
|
|
30
|
+
dsh plugin --profile penpot add github:vladtsvirko/dsh-penpot-plugin#v0.3.0
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The bundle's own patch inserts the `dsh-penpot` row and reads both values from
|
|
34
|
+
the runtime environment, so there is no configuration file to edit:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
export PENPOT_API_URL='http://192.168.2.50:8090' # /api/rpc suffix is optional
|
|
38
|
+
export PENPOT_ACCESS_TOKEN='...'
|
|
39
|
+
dsh --profile penpot
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Export the variables in the environment that starts the runtime. A shell export
|
|
43
|
+
made after the runtime is already running does not reach it.
|
|
44
|
+
|
|
45
|
+
## Verify
|
|
46
|
+
|
|
47
|
+
Ask the agent for `penpot_status`. It reports either `api <url>` or
|
|
48
|
+
`none — not configured`, naming the missing variables. The other tools return
|
|
49
|
+
one readable error when the backend is unconfigured; the plugin itself still
|
|
50
|
+
loads, so a missing token never takes the runtime down.
|
|
51
|
+
|
|
52
|
+
## Update and remove
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
dsh plugin --profile penpot remove dsh-penpot-plugin
|
|
56
|
+
dsh plugin --profile penpot add dsh-penpot-plugin@<new>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The host's plugin manager refuses to install a git package over an installed
|
|
60
|
+
one, so removal comes first. Restart the runtime if the command reports
|
|
61
|
+
`restart-required`. In a setup deployment the bot does this through the plugin
|
|
62
|
+
manager and wraps it in a rollback trial (`/system update penpot`); the package
|
|
63
|
+
and the row are exactly the same.
|
|
64
|
+
|
|
65
|
+
## What the setup adds
|
|
66
|
+
|
|
67
|
+
| Setup feature | Standalone equivalent |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `install.sh --design penpot` installs Penpot and the token | run Penpot yourself |
|
|
70
|
+
| managed `dsh-penpot` row in `cordis.patch.yml` | the bundle's own insert row |
|
|
71
|
+
| `/system components` enable/disable | `dsh plugin … add/remove` |
|
|
72
|
+
| `/system update penpot` with a trial | the two `dsh plugin` commands above |
|
|
73
|
+
| `/system rollback` | keep the previous tag; re-add it |
|
|
74
|
+
|
|
75
|
+
Nothing in the tool surface changes. The setup adds lifecycle and packaging, not
|
|
76
|
+
capability.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct HTTP backend: Penpot's own RPC, authenticated with an access token.
|
|
3
|
+
*
|
|
4
|
+
* This is a port of `design/penpot.mjs` from the dsh-tg-docker-setup CLI, kept
|
|
5
|
+
* here as a typed module so the plugin has no dependency on that repository.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-penpot-plugin/api-backend
|
|
8
|
+
*/
|
|
9
|
+
import type { PenpotBackend } from './backend.js';
|
|
10
|
+
export interface ApiBackendOptions {
|
|
11
|
+
apiUrl: string;
|
|
12
|
+
token: string;
|
|
13
|
+
/** Injectable for tests. */
|
|
14
|
+
fetchImpl?: typeof fetch;
|
|
15
|
+
}
|
|
16
|
+
/** Strip a trailing /api/rpc or slash so any pasted form works. */
|
|
17
|
+
export declare function normalizeBase(url: string): string;
|
|
18
|
+
export declare class PenpotApiError extends Error {
|
|
19
|
+
readonly status: number;
|
|
20
|
+
constructor(status: number, detail: string);
|
|
21
|
+
}
|
|
22
|
+
/** Read an SSE body into `{ event, data }` records, ignoring comments. */
|
|
23
|
+
export declare const readSse: (response: Response) => Promise<Array<{
|
|
24
|
+
event: string;
|
|
25
|
+
data: unknown;
|
|
26
|
+
}>>;
|
|
27
|
+
export declare function createApiBackend(options: ApiBackendOptions): PenpotBackend;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct HTTP backend: Penpot's own RPC, authenticated with an access token.
|
|
3
|
+
*
|
|
4
|
+
* This is a port of `design/penpot.mjs` from the dsh-tg-docker-setup CLI, kept
|
|
5
|
+
* here as a typed module so the plugin has no dependency on that repository.
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-penpot-plugin/api-backend
|
|
8
|
+
*/
|
|
9
|
+
import { readFile } from 'node:fs/promises';
|
|
10
|
+
import { basename } from 'node:path';
|
|
11
|
+
/** Strip a trailing /api/rpc or slash so any pasted form works. */
|
|
12
|
+
export function normalizeBase(url) {
|
|
13
|
+
return url.replace(/\/+$/, '').replace(/\/api\/rpc$/, '');
|
|
14
|
+
}
|
|
15
|
+
export class PenpotApiError extends Error {
|
|
16
|
+
status;
|
|
17
|
+
constructor(status, detail) {
|
|
18
|
+
super(`Penpot answered ${String(status)}${detail === '' ? '' : `: ${detail}`}`);
|
|
19
|
+
this.name = 'PenpotApiError';
|
|
20
|
+
this.status = status;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const asRefs = (value) => {
|
|
24
|
+
if (!Array.isArray(value))
|
|
25
|
+
return [];
|
|
26
|
+
return value
|
|
27
|
+
.filter((item) => typeof item === 'object' && item !== null)
|
|
28
|
+
.map((item) => ({ id: String(item['id'] ?? ''), name: String(item['name'] ?? '') }));
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Penpot's export/import endpoints answer as Server-Sent Events, and the event
|
|
32
|
+
* data is transit-encoded even when the request asked for JSON. The only
|
|
33
|
+
* markers these endpoints emit are `~:` (keyword key) and `~u` (uuid), so a
|
|
34
|
+
* small normalizer is enough and avoids a transit dependency.
|
|
35
|
+
*/
|
|
36
|
+
const decodeTransit = (value) => {
|
|
37
|
+
if (typeof value === 'string') {
|
|
38
|
+
if (value.startsWith('~u'))
|
|
39
|
+
return value.slice(2);
|
|
40
|
+
if (value.startsWith('~:'))
|
|
41
|
+
return value.slice(2);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(value))
|
|
45
|
+
return value.map(decodeTransit);
|
|
46
|
+
if (value !== null && typeof value === 'object') {
|
|
47
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key.startsWith('~:') ? key.slice(2) : key, decodeTransit(item)]));
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
};
|
|
51
|
+
/** Read an SSE body into `{ event, data }` records, ignoring comments. */
|
|
52
|
+
export const readSse = async (response) => {
|
|
53
|
+
const text = await response.text();
|
|
54
|
+
const events = [];
|
|
55
|
+
for (const block of text.split(/\r?\n\r?\n/)) {
|
|
56
|
+
let event = 'message';
|
|
57
|
+
const data = [];
|
|
58
|
+
for (const line of block.split(/\r?\n/)) {
|
|
59
|
+
if (line.startsWith('event:'))
|
|
60
|
+
event = line.slice(6).trim();
|
|
61
|
+
else if (line.startsWith('data:'))
|
|
62
|
+
data.push(line.slice(5).trim());
|
|
63
|
+
}
|
|
64
|
+
if (data.length === 0)
|
|
65
|
+
continue;
|
|
66
|
+
const raw = data.join('\n');
|
|
67
|
+
let parsed = raw;
|
|
68
|
+
try {
|
|
69
|
+
parsed = decodeTransit(JSON.parse(raw));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// A non-JSON payload stays a string; the caller decides what it means.
|
|
73
|
+
}
|
|
74
|
+
events.push({ event, data: parsed });
|
|
75
|
+
}
|
|
76
|
+
return events;
|
|
77
|
+
};
|
|
78
|
+
/** The payload of the last `end` event, or undefined when the stream had none. */
|
|
79
|
+
const lastEnd = (events) => {
|
|
80
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
81
|
+
if (events[index]?.event === 'end')
|
|
82
|
+
return events[index]?.data;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
};
|
|
86
|
+
const failOnErrorEvent = (events) => {
|
|
87
|
+
const error = events.find((item) => item.event === 'error');
|
|
88
|
+
if (error !== undefined) {
|
|
89
|
+
throw new PenpotApiError(200, JSON.stringify(error.data).slice(0, 300));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export function createApiBackend(options) {
|
|
93
|
+
const base = normalizeBase(options.apiUrl);
|
|
94
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
95
|
+
const headers = { authorization: `Token ${options.token}`, accept: 'application/json' };
|
|
96
|
+
const rpc = async (command, body = {}) => {
|
|
97
|
+
const response = await fetchImpl(`${base}/api/rpc/command/${command}`, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: { ...headers, 'content-type': 'application/json' },
|
|
100
|
+
body: JSON.stringify(body),
|
|
101
|
+
});
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
const text = await response.text().catch(() => '');
|
|
104
|
+
throw new PenpotApiError(response.status, text.slice(0, 300).trim());
|
|
105
|
+
}
|
|
106
|
+
return response;
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
kind: 'api',
|
|
110
|
+
label: `api ${base}`,
|
|
111
|
+
async listTeams() {
|
|
112
|
+
return asRefs(await (await rpc('get-teams')).json());
|
|
113
|
+
},
|
|
114
|
+
async listProjects(teamId) {
|
|
115
|
+
const command = teamId === undefined ? 'get-all-projects' : 'get-projects';
|
|
116
|
+
return asRefs(await (await rpc(command, teamId === undefined ? {} : { 'team-id': teamId })).json());
|
|
117
|
+
},
|
|
118
|
+
async listFiles(projectId) {
|
|
119
|
+
return asRefs(await (await rpc('get-project-files', { 'project-id': projectId })).json());
|
|
120
|
+
},
|
|
121
|
+
async searchFiles(teamId, term) {
|
|
122
|
+
return asRefs(await (await rpc('search-files', { 'team-id': teamId, 'search-term': term })).json());
|
|
123
|
+
},
|
|
124
|
+
async fileSummary(fileId) {
|
|
125
|
+
const raw = await (await rpc('get-file-summary', { id: fileId })).json();
|
|
126
|
+
const pages = Array.isArray(raw['pages']) ? asRefs(raw['pages']) : undefined;
|
|
127
|
+
return { id: String(raw['id'] ?? fileId), name: String(raw['name'] ?? ''), ...(pages === undefined ? {} : { pages }), raw };
|
|
128
|
+
},
|
|
129
|
+
async exportFile(fileId) {
|
|
130
|
+
// `export-binfile` streams SSE and ends with the URL of the exported zip.
|
|
131
|
+
const response = await rpc('export-binfile', {
|
|
132
|
+
'file-id': fileId,
|
|
133
|
+
'include-libraries': false,
|
|
134
|
+
'embed-assets': true,
|
|
135
|
+
});
|
|
136
|
+
const events = await readSse(response);
|
|
137
|
+
failOnErrorEvent(events);
|
|
138
|
+
const url = lastEnd(events);
|
|
139
|
+
if (typeof url !== 'string' || url.length === 0) {
|
|
140
|
+
throw new PenpotApiError(response.status, 'the export finished without a download URL');
|
|
141
|
+
}
|
|
142
|
+
const download = await fetchImpl(url);
|
|
143
|
+
if (!download.ok) {
|
|
144
|
+
throw new PenpotApiError(download.status, `the export download failed (${String(download.status)})`);
|
|
145
|
+
}
|
|
146
|
+
return new Uint8Array(await download.arrayBuffer());
|
|
147
|
+
},
|
|
148
|
+
async importFile({ path, projectId, fileId, name }) {
|
|
149
|
+
const bytes = await readFile(path);
|
|
150
|
+
const form = new FormData();
|
|
151
|
+
if (name !== undefined)
|
|
152
|
+
form.set('name', name);
|
|
153
|
+
if (projectId !== undefined)
|
|
154
|
+
form.set('project-id', projectId);
|
|
155
|
+
if (fileId !== undefined)
|
|
156
|
+
form.set('file-id', fileId);
|
|
157
|
+
form.set('file', new Blob([bytes], { type: 'application/octet-stream' }), basename(path));
|
|
158
|
+
const response = await fetchImpl(`${base}/api/rpc/command/import-binfile`, { method: 'POST', headers, body: form });
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
const text = await response.text().catch(() => '');
|
|
161
|
+
throw new PenpotApiError(response.status, text.slice(0, 300).trim());
|
|
162
|
+
}
|
|
163
|
+
// The stream ends with the id(s) of the imported file(s).
|
|
164
|
+
const events = await readSse(response);
|
|
165
|
+
failOnErrorEvent(events);
|
|
166
|
+
const result = lastEnd(events);
|
|
167
|
+
const first = Array.isArray(result) ? result[0] : result;
|
|
168
|
+
return { id: typeof first === 'string' && first.length > 0 ? first : undefined, raw: events };
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=api-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-backend.js","sourceRoot":"","sources":["../src/api-backend.ts"],"names":[],"mappings":"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"}
|
package/lib/backend.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The backend seam.
|
|
3
|
+
*
|
|
4
|
+
* Penpot is reached over its HTTP RPC with an access token. The plugin owns one
|
|
5
|
+
* small interface so the model-facing tools stay independent of the transport
|
|
6
|
+
* details (and of future changes to them).
|
|
7
|
+
*
|
|
8
|
+
* Write operations are deliberately absent from v0.1: Penpot's `update-file`
|
|
9
|
+
* needs its change format, and the read/export/import set is the part that can
|
|
10
|
+
* be proven first.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-penpot-plugin/backend
|
|
13
|
+
*/
|
|
14
|
+
/** One addressable Penpot object, reduced to what a tool reply needs. */
|
|
15
|
+
export interface PenpotRef {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
/** A file summary: enough to identify a design without shipping its contents. */
|
|
20
|
+
export interface PenpotFileSummary {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
/** Pages in the file, when the backend reports them. */
|
|
24
|
+
pages?: PenpotRef[];
|
|
25
|
+
/** Backend-specific extras, passed through as-is. */
|
|
26
|
+
raw?: unknown;
|
|
27
|
+
}
|
|
28
|
+
/** What a backend must provide. Every method throws on refusal. */
|
|
29
|
+
export interface PenpotBackend {
|
|
30
|
+
/** Which implementation is live; surfaced by the status tool and `/system status`-style checks. */
|
|
31
|
+
readonly kind: 'api';
|
|
32
|
+
/** Short human label, e.g. `api http://192.168.2.50:8090`. */
|
|
33
|
+
readonly label: string;
|
|
34
|
+
listTeams(): Promise<PenpotRef[]>;
|
|
35
|
+
listProjects(teamId?: string): Promise<PenpotRef[]>;
|
|
36
|
+
listFiles(projectId: string): Promise<PenpotRef[]>;
|
|
37
|
+
searchFiles(teamId: string, term: string): Promise<PenpotRef[]>;
|
|
38
|
+
fileSummary(fileId: string): Promise<PenpotFileSummary>;
|
|
39
|
+
/** Whole-file export as `.penpot` bytes. */
|
|
40
|
+
exportFile(fileId: string): Promise<Uint8Array>;
|
|
41
|
+
/** Import a `.penpot` file, either as a new file or in place. */
|
|
42
|
+
importFile(input: {
|
|
43
|
+
path: string;
|
|
44
|
+
projectId?: string;
|
|
45
|
+
fileId?: string;
|
|
46
|
+
name?: string;
|
|
47
|
+
}): Promise<{
|
|
48
|
+
id?: string;
|
|
49
|
+
raw?: unknown;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
package/lib/backend.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The backend seam.
|
|
3
|
+
*
|
|
4
|
+
* Penpot is reached over its HTTP RPC with an access token. The plugin owns one
|
|
5
|
+
* small interface so the model-facing tools stay independent of the transport
|
|
6
|
+
* details (and of future changes to them).
|
|
7
|
+
*
|
|
8
|
+
* Write operations are deliberately absent from v0.1: Penpot's `update-file`
|
|
9
|
+
* needs its change format, and the read/export/import set is the part that can
|
|
10
|
+
* be proven first.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-penpot-plugin/backend
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-penpot — Penpot tools for a DeepSeek Harness runtime.
|
|
3
|
+
*
|
|
4
|
+
* The plugin is a thin shell over Penpot's HTTP RPC API: one local backend,
|
|
5
|
+
* one small stable tool surface. There is deliberately no MCP path; see
|
|
6
|
+
* docs/DESIGN.md in the setup repository.
|
|
7
|
+
*
|
|
8
|
+
* v0.1 is read + export. Writing (Penpot's `update-file` change format) is the
|
|
9
|
+
* open design question; see docs/DESIGN.md.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-penpot-plugin
|
|
12
|
+
*/
|
|
13
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
14
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
15
|
+
export declare const name = "dsh-penpot";
|
|
16
|
+
export declare const inject: string[];
|
|
17
|
+
export declare const Config: Schema<Schemastery.ObjectS<NoInfer<{
|
|
18
|
+
apiUrl: Schema<string, string, "defined">;
|
|
19
|
+
token: Schema<string, string, "defined">;
|
|
20
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
21
|
+
apiUrl: Schema<string, string, "defined">;
|
|
22
|
+
token: Schema<string, string, "defined">;
|
|
23
|
+
}>>, "plain">;
|
|
24
|
+
export interface PenpotPluginConfig {
|
|
25
|
+
apiUrl?: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function apply(ctx: Context, config: PenpotPluginConfig): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-penpot — Penpot tools for a DeepSeek Harness runtime.
|
|
3
|
+
*
|
|
4
|
+
* The plugin is a thin shell over Penpot's HTTP RPC API: one local backend,
|
|
5
|
+
* one small stable tool surface. There is deliberately no MCP path; see
|
|
6
|
+
* docs/DESIGN.md in the setup repository.
|
|
7
|
+
*
|
|
8
|
+
* v0.1 is read + export. Writing (Penpot's `update-file` change format) is the
|
|
9
|
+
* open design question; see docs/DESIGN.md.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-penpot-plugin
|
|
12
|
+
*/
|
|
13
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
14
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
15
|
+
import { createApiBackend } from './api-backend.js';
|
|
16
|
+
export const name = 'dsh-penpot';
|
|
17
|
+
// `ctx.tools` is a declared service: Cordis refuses the property without an
|
|
18
|
+
// injection declaration, even when the profile has the service mounted. This is
|
|
19
|
+
// what makes the plugin load in a plain profile, not only beside the setup.
|
|
20
|
+
export const inject = ['tools'];
|
|
21
|
+
export const Config = Schema.object({
|
|
22
|
+
apiUrl: Schema.string().default(''),
|
|
23
|
+
token: Schema.string().default(''),
|
|
24
|
+
});
|
|
25
|
+
/** Render one list result: id and name per line, backend in parentheses. */
|
|
26
|
+
function describeRefs(value) {
|
|
27
|
+
return value.items.length === 0
|
|
28
|
+
? `no results (${value.backend})`
|
|
29
|
+
: `${value.items.map((item) => `${item.id}\t${item.name}`).join('\n')}\n(${value.backend})`;
|
|
30
|
+
}
|
|
31
|
+
export function apply(ctx, config) {
|
|
32
|
+
const configured = typeof config.apiUrl === 'string' && config.apiUrl.length > 0
|
|
33
|
+
&& typeof config.token === 'string' && config.token.length > 0;
|
|
34
|
+
const backend = configured
|
|
35
|
+
? createApiBackend({ apiUrl: config.apiUrl, token: config.token })
|
|
36
|
+
: undefined;
|
|
37
|
+
const requireBackend = () => {
|
|
38
|
+
if (backend === undefined) {
|
|
39
|
+
throw new Error('Penpot is not configured: set PENPOT_API_URL and PENPOT_ACCESS_TOKEN, then restart dsh');
|
|
40
|
+
}
|
|
41
|
+
return backend;
|
|
42
|
+
};
|
|
43
|
+
ctx.tools.register(defineTool({
|
|
44
|
+
name: 'penpot_status',
|
|
45
|
+
description: 'Report whether the Penpot HTTP API is configured. Call it when a Penpot tool fails.',
|
|
46
|
+
parameters: {},
|
|
47
|
+
output: {
|
|
48
|
+
schema: {
|
|
49
|
+
type: 'object',
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
properties: {
|
|
52
|
+
backend: { type: 'string', required: true, description: 'api, or none when not configured.' },
|
|
53
|
+
label: { type: 'string', required: true, description: 'Human-readable endpoint, e.g. "api http://host:8090".' },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
render: (_args, value) => [{ type: 'text', text: `${value.backend} — ${value.label}` }],
|
|
57
|
+
},
|
|
58
|
+
async execute() {
|
|
59
|
+
return backend === undefined
|
|
60
|
+
? { backend: 'none', label: 'not configured (PENPOT_API_URL / PENPOT_ACCESS_TOKEN)' }
|
|
61
|
+
: { backend: requireBackend().kind, label: requireBackend().label };
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
64
|
+
ctx.tools.register(defineTool({
|
|
65
|
+
name: 'penpot_teams',
|
|
66
|
+
description: 'List the Penpot teams the configured account can see. Start here when you do not know any ids.',
|
|
67
|
+
parameters: {},
|
|
68
|
+
output: {
|
|
69
|
+
schema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
additionalProperties: false,
|
|
72
|
+
properties: {
|
|
73
|
+
backend: { type: 'string', required: true, description: 'Which backend answered: api.' },
|
|
74
|
+
items: {
|
|
75
|
+
type: 'array',
|
|
76
|
+
required: true,
|
|
77
|
+
description: 'Penpot objects, id and name only.',
|
|
78
|
+
items: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: {
|
|
82
|
+
id: { type: 'string', required: true, description: 'Penpot uuid.' },
|
|
83
|
+
name: { type: 'string', required: true, description: 'Display name.' },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
render: (_args, value) => [{ type: 'text', text: describeRefs(value) }],
|
|
90
|
+
},
|
|
91
|
+
async execute() {
|
|
92
|
+
return { backend: requireBackend().kind, items: await requireBackend().listTeams() };
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
ctx.tools.register(defineTool({
|
|
96
|
+
name: 'penpot_projects',
|
|
97
|
+
description: 'List Penpot projects, optionally restricted to one team.',
|
|
98
|
+
parameters: {
|
|
99
|
+
teamId: { type: 'string', description: 'Team uuid from penpot_teams. Omit for every project the account can see.' },
|
|
100
|
+
},
|
|
101
|
+
output: {
|
|
102
|
+
schema: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
additionalProperties: false,
|
|
105
|
+
properties: {
|
|
106
|
+
backend: { type: 'string', required: true, description: 'Which backend answered: api.' },
|
|
107
|
+
items: {
|
|
108
|
+
type: 'array',
|
|
109
|
+
required: true,
|
|
110
|
+
description: 'Penpot objects, id and name only.',
|
|
111
|
+
items: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
additionalProperties: false,
|
|
114
|
+
properties: {
|
|
115
|
+
id: { type: 'string', required: true, description: 'Penpot uuid.' },
|
|
116
|
+
name: { type: 'string', required: true, description: 'Display name.' },
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
render: (_args, value) => [{ type: 'text', text: describeRefs(value) }],
|
|
123
|
+
},
|
|
124
|
+
async execute(args) {
|
|
125
|
+
return { backend: requireBackend().kind, items: await requireBackend().listProjects(args.teamId) };
|
|
126
|
+
},
|
|
127
|
+
}));
|
|
128
|
+
ctx.tools.register(defineTool({
|
|
129
|
+
name: 'penpot_files',
|
|
130
|
+
description: 'List the design files in a Penpot project.',
|
|
131
|
+
parameters: {
|
|
132
|
+
projectId: { type: 'string', required: true, description: 'Project uuid from penpot_projects.' },
|
|
133
|
+
},
|
|
134
|
+
output: {
|
|
135
|
+
schema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
additionalProperties: false,
|
|
138
|
+
properties: {
|
|
139
|
+
backend: { type: 'string', required: true, description: 'Which backend answered: api.' },
|
|
140
|
+
items: {
|
|
141
|
+
type: 'array',
|
|
142
|
+
required: true,
|
|
143
|
+
description: 'Penpot objects, id and name only.',
|
|
144
|
+
items: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
additionalProperties: false,
|
|
147
|
+
properties: {
|
|
148
|
+
id: { type: 'string', required: true, description: 'Penpot uuid.' },
|
|
149
|
+
name: { type: 'string', required: true, description: 'Display name.' },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
render: (_args, value) => [{ type: 'text', text: describeRefs(value) }],
|
|
156
|
+
},
|
|
157
|
+
async execute(args) {
|
|
158
|
+
return { backend: requireBackend().kind, items: await requireBackend().listFiles(args.projectId) };
|
|
159
|
+
},
|
|
160
|
+
}));
|
|
161
|
+
ctx.tools.register(defineTool({
|
|
162
|
+
name: 'penpot_search',
|
|
163
|
+
description: 'Search Penpot design files by name or content term. Penpot searches within one team, so pass teamId or let the first team be used.',
|
|
164
|
+
parameters: {
|
|
165
|
+
query: { type: 'string', required: true, description: 'Search term.' },
|
|
166
|
+
teamId: { type: 'string', description: 'Team uuid from penpot_teams; defaults to the first team.' },
|
|
167
|
+
},
|
|
168
|
+
output: {
|
|
169
|
+
schema: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
additionalProperties: false,
|
|
172
|
+
properties: {
|
|
173
|
+
backend: { type: 'string', required: true, description: 'Which backend answered: api.' },
|
|
174
|
+
items: {
|
|
175
|
+
type: 'array',
|
|
176
|
+
required: true,
|
|
177
|
+
description: 'Penpot objects, id and name only.',
|
|
178
|
+
items: {
|
|
179
|
+
type: 'object',
|
|
180
|
+
additionalProperties: false,
|
|
181
|
+
properties: {
|
|
182
|
+
id: { type: 'string', required: true, description: 'Penpot uuid.' },
|
|
183
|
+
name: { type: 'string', required: true, description: 'Display name.' },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
render: (_args, value) => [{ type: 'text', text: describeRefs(value) }],
|
|
190
|
+
},
|
|
191
|
+
async execute(args) {
|
|
192
|
+
const backend = requireBackend();
|
|
193
|
+
const teamId = args.teamId ?? (await backend.listTeams())[0]?.id;
|
|
194
|
+
if (teamId === undefined || teamId.length === 0) {
|
|
195
|
+
throw new Error('no Penpot team found — create one in Penpot before searching');
|
|
196
|
+
}
|
|
197
|
+
return { backend: backend.kind, items: await backend.searchFiles(teamId, args.query) };
|
|
198
|
+
},
|
|
199
|
+
}));
|
|
200
|
+
ctx.tools.register(defineTool({
|
|
201
|
+
name: 'penpot_file',
|
|
202
|
+
description: 'Show one design file: its id, name and pages. Use it to find the page ids an export or a future write needs.',
|
|
203
|
+
parameters: {
|
|
204
|
+
fileId: { type: 'string', required: true, description: 'File uuid from penpot_files or penpot_search.' },
|
|
205
|
+
},
|
|
206
|
+
output: {
|
|
207
|
+
schema: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
additionalProperties: false,
|
|
210
|
+
properties: {
|
|
211
|
+
backend: { type: 'string', required: true, description: 'api, or none when not configured.' },
|
|
212
|
+
file: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
required: true,
|
|
215
|
+
additionalProperties: false,
|
|
216
|
+
properties: {
|
|
217
|
+
id: { type: 'string', required: true, description: 'File uuid.' },
|
|
218
|
+
name: { type: 'string', required: true, description: 'File name.' },
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
render: (_args, value) => [{ type: 'text', text: `${value.file.id}\t${value.file.name} (${value.backend})` }],
|
|
224
|
+
},
|
|
225
|
+
async execute(args) {
|
|
226
|
+
const summary = await requireBackend().fileSummary(args.fileId);
|
|
227
|
+
return { backend: requireBackend().kind, file: { id: summary.id, name: summary.name } };
|
|
228
|
+
},
|
|
229
|
+
}));
|
|
230
|
+
ctx.tools.register(defineTool({
|
|
231
|
+
name: 'penpot_export',
|
|
232
|
+
description: 'Export a design file to a .penpot file on disk (a zip of Penpot JSON). Read it with the normal file tools, then import it back with penpot_import.',
|
|
233
|
+
parameters: {
|
|
234
|
+
fileId: { type: 'string', required: true, description: 'File uuid.' },
|
|
235
|
+
path: { type: 'string', required: true, description: 'Absolute path to write.' },
|
|
236
|
+
},
|
|
237
|
+
output: {
|
|
238
|
+
schema: {
|
|
239
|
+
type: 'object',
|
|
240
|
+
additionalProperties: false,
|
|
241
|
+
properties: {
|
|
242
|
+
path: { type: 'string', required: true, description: 'Where the file was written.' },
|
|
243
|
+
bytes: { type: 'number', required: true, description: 'Size in bytes.' },
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
render: (_args, value) => [{ type: 'text', text: `${value.path} (${String(value.bytes)} bytes)` }],
|
|
247
|
+
},
|
|
248
|
+
async execute(args) {
|
|
249
|
+
const bytes = await requireBackend().exportFile(args.fileId);
|
|
250
|
+
const { writeFile } = await import('node:fs/promises');
|
|
251
|
+
await writeFile(args.path, bytes);
|
|
252
|
+
return { path: args.path, bytes: bytes.byteLength };
|
|
253
|
+
},
|
|
254
|
+
}));
|
|
255
|
+
ctx.tools.register(defineTool({
|
|
256
|
+
name: 'penpot_import',
|
|
257
|
+
description: 'Import a .penpot file (a zip of Penpot JSON) as a new file in a project, or replace an existing file. Use penpot_export to produce the input.',
|
|
258
|
+
parameters: {
|
|
259
|
+
path: { type: 'string', required: true, description: 'Absolute path of the .penpot file to upload.' },
|
|
260
|
+
projectId: { type: 'string', description: 'Target project uuid from penpot_projects. Required unless fileId is given.' },
|
|
261
|
+
fileId: { type: 'string', description: 'Existing file uuid to replace. Omit to create a new file.' },
|
|
262
|
+
name: { type: 'string', description: 'Display name for a new file. Ignored when fileId is given.' },
|
|
263
|
+
},
|
|
264
|
+
output: {
|
|
265
|
+
schema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
additionalProperties: false,
|
|
268
|
+
properties: {
|
|
269
|
+
id: { type: 'string', required: true, description: 'Penpot uuid of the imported file.' },
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
render: (_args, value) => [{ type: 'text', text: `imported ${value.id}` }],
|
|
273
|
+
},
|
|
274
|
+
async execute(args) {
|
|
275
|
+
if ((args.projectId === undefined || args.projectId.length === 0)
|
|
276
|
+
&& (args.fileId === undefined || args.fileId.length === 0)) {
|
|
277
|
+
throw new Error('penpot_import needs a projectId for a new file, or a fileId to replace one');
|
|
278
|
+
}
|
|
279
|
+
const result = await requireBackend().importFile({
|
|
280
|
+
path: args.path,
|
|
281
|
+
...(args.projectId === undefined ? {} : { projectId: args.projectId }),
|
|
282
|
+
...(args.fileId === undefined ? {} : { fileId: args.fileId }),
|
|
283
|
+
...(args.name === undefined ? {} : { name: args.name }),
|
|
284
|
+
});
|
|
285
|
+
// Penpot's import answers the new file id. A backend that cannot report
|
|
286
|
+
// one has still done the import, so name what is known rather than lie.
|
|
287
|
+
return { id: result.id ?? 'unknown (Penpot did not return a file id)' };
|
|
288
|
+
},
|
|
289
|
+
}));
|
|
290
|
+
}
|
|
291
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-penpot-plugin",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Penpot for DeepSeek Harness — read, export and import design files from a DSH agent over Penpot's local HTTP RPC API.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"lib",
|
|
10
|
+
"cordis.patch.yml",
|
|
11
|
+
"README.md",
|
|
12
|
+
"CHANGELOG.md",
|
|
13
|
+
"COMPATIBILITY.md",
|
|
14
|
+
"docs"
|
|
15
|
+
],
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/vladtsvirko/dsh-penpot-plugin.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/vladtsvirko/dsh-penpot-plugin#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/vladtsvirko/dsh-penpot-plugin/issues"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"prepare": "npm run build",
|
|
30
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build",
|
|
33
|
+
"clean": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\"",
|
|
34
|
+
"prebuild": "npm run clean"
|
|
35
|
+
},
|
|
36
|
+
"dsh": {
|
|
37
|
+
"bundle": {
|
|
38
|
+
"patch": "./cordis.patch.yml"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
43
|
+
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.6 <0.3.0-0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
47
|
+
"@deepseek-ai/dsh-tools": "0.2.1-rc.2",
|
|
48
|
+
"@types/node": "^24.0.0",
|
|
49
|
+
"typescript": "^5.9.0",
|
|
50
|
+
"vitest": "^4.1.10"
|
|
51
|
+
},
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"keywords": [
|
|
54
|
+
"dsh",
|
|
55
|
+
"dsh-plugin",
|
|
56
|
+
"deepseek-harness",
|
|
57
|
+
"penpot",
|
|
58
|
+
"design"
|
|
59
|
+
],
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|