dsh-penpot-plugin 0.0.0-stage → 0.3.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 ADDED
@@ -0,0 +1,64 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0
4
+
5
+ - **npm distribution.** The package is published as
6
+ `dsh-penpot-plugin@<version>`; the registry tarball contains `lib/`, so
7
+ installs no longer run a build script. The release workflow publishes a tag
8
+ with provenance. The setup pins this npm spec and keeps the GitHub tag as a
9
+ fallback.
10
+ - **`penpot_import`.** The export counterpart is now a tool: upload a `.penpot`
11
+ file as a new file (`projectId`) or replace an existing one (`fileId`). The
12
+ backend already had the multipart `import-binfile` call; the setup CLI was the
13
+ only caller.
14
+ - **Bundle patch corrected.** `cordis.patch.yml` still passed `backend: auto`
15
+ and `mcpServer`, which 0.2.0 removed. The row now declares only `apiUrl` and
16
+ `token`, so a standalone install no longer starts with a stale config key.
17
+ - **Builds clean before packaging.** `lib/` is removed before every build, so
18
+ compiled files from removed modules (the old MCP backend) can never enter a
19
+ tarball.
20
+ - **The package description no longer claims an MCP backend.** It was stale
21
+ since 0.2.0 and is the text GitHub and npm show.
22
+ - **Standalone is documented as a first-class path** (`docs/STANDALONE.md`): no
23
+ setup, no managed row, same tools. The setup remains the convenience wrapper,
24
+ not a requirement.
25
+ - **Load fix.** The plugin used `ctx.tools` without declaring
26
+ `inject = ['tools']`; Cordis refuses that property, so a standalone profile
27
+ failed at load with `cannot get property "tools" without inject`. The setup
28
+ masked it only while the plugin happened not to be mounted. Declared now.
29
+ - Tool-surface tests cover the service declaration, the always-registered set,
30
+ the unconfigured status, and the import destination check.
31
+
32
+ ## 0.2.0
33
+
34
+ - **MCP backend removed.** The plugin talks to Penpot's local HTTP RPC only.
35
+ `backend`, `mcpServer` and the `mcp__<server>__*` adapter are gone; the
36
+ read-only MCP path could not export and could silently replace the API via
37
+ `auto`.
38
+ - The plugin now loads even when `PENPOT_API_URL`/`PENPOT_ACCESS_TOKEN` are
39
+ empty: `penpot_status` reports `none`, the other tools throw one readable
40
+ configuration error.
41
+ - README/DESIGN updated for the systemd/Podman setup (`--design penpot`).
42
+
43
+ ## 0.1.2
44
+
45
+ Docs only. The setup path now says `/system components` (the command was renamed
46
+ from `/system manage`).
47
+
48
+ ## 0.1.1
49
+
50
+ - Docs only. The install section now leads with the DSH setup's design
51
+ component (`install.sh --design docker` / `/system manage`), pins the manual
52
+ install at a tag, and explains the pnpm `allowBuilds` gate that otherwise
53
+ aborts a git install. Credentials are described as the runtime process's
54
+ environment, not a shell export.
55
+ - `COMPATIBILITY.md` records that the setup pins this plugin and the Penpot
56
+ images, so a ref or image bump is a setup release.
57
+
58
+ ## 0.1.0
59
+
60
+ - First cut: `penpot_status`, `penpot_teams`, `penpot_projects`,
61
+ `penpot_files`, `penpot_search`, `penpot_file`, `penpot_export`.
62
+ - Two backends behind one interface: the HTTP API with an access token, and an
63
+ MCP server already registered in the runtime. `auto` prefers MCP read tools.
64
+ - Export writes a `.penpot` file; import is not exposed as a tool yet.
@@ -0,0 +1,19 @@
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.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 |
10
+ | 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` |
11
+ | 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 |
12
+ | 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 |
13
+ | 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 |
14
+
15
+ The setup's design component is the reference deployment: `plugins.json` pins
16
+ this plugin at `v0.3.0` and `components.json` pins the Penpot images at `2.18`.
17
+ The plugin version is independent of the setup release; the setup pin is what a
18
+ fresh deployment installs, and a newer compatible tag installed from the bot
19
+ 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
- # Temporary Holding Version
1
+ # dsh-penpot-plugin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.
@@ -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,22 @@
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
+ export declare function createApiBackend(options: ApiBackendOptions): PenpotBackend;
@@ -0,0 +1,91 @@
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
+ export function createApiBackend(options) {
31
+ const base = normalizeBase(options.apiUrl);
32
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
33
+ const headers = { authorization: `Token ${options.token}` };
34
+ const rpc = async (command, body = {}) => {
35
+ const response = await fetchImpl(`${base}/api/rpc/command/${command}`, {
36
+ method: 'POST',
37
+ headers: { ...headers, 'content-type': 'application/json' },
38
+ body: JSON.stringify(body),
39
+ });
40
+ if (!response.ok) {
41
+ const text = await response.text().catch(() => '');
42
+ throw new PenpotApiError(response.status, text.slice(0, 300).trim());
43
+ }
44
+ return response;
45
+ };
46
+ return {
47
+ kind: 'api',
48
+ label: `api ${base}`,
49
+ async listTeams() {
50
+ return asRefs(await (await rpc('get-teams')).json());
51
+ },
52
+ async listProjects(teamId) {
53
+ const command = teamId === undefined ? 'get-all-projects' : 'get-projects';
54
+ return asRefs(await (await rpc(command, teamId === undefined ? {} : { 'team-id': teamId })).json());
55
+ },
56
+ async listFiles(projectId) {
57
+ return asRefs(await (await rpc('get-project-files', { 'project-id': projectId })).json());
58
+ },
59
+ async searchFiles(term) {
60
+ return asRefs(await (await rpc('search-files', { 'search-term': term })).json());
61
+ },
62
+ async fileSummary(fileId) {
63
+ const raw = await (await rpc('get-file-summary', { id: fileId })).json();
64
+ const pages = Array.isArray(raw['pages']) ? asRefs(raw['pages']) : undefined;
65
+ return { id: String(raw['id'] ?? fileId), name: String(raw['name'] ?? ''), ...(pages === undefined ? {} : { pages }), raw };
66
+ },
67
+ async exportFile(fileId) {
68
+ const response = await rpc('export-binfile', { fileId, includeLibraries: false, embedAssets: true });
69
+ return new Uint8Array(await response.arrayBuffer());
70
+ },
71
+ async importFile({ path, projectId, fileId, name }) {
72
+ const bytes = await readFile(path);
73
+ const form = new FormData();
74
+ if (name !== undefined)
75
+ form.set('name', name);
76
+ if (projectId !== undefined)
77
+ form.set('project-id', projectId);
78
+ if (fileId !== undefined)
79
+ form.set('file-id', fileId);
80
+ form.set('file', new Blob([bytes], { type: 'application/octet-stream' }), basename(path));
81
+ const response = await fetchImpl(`${base}/api/rpc/command/import-binfile`, { method: 'POST', headers, body: form });
82
+ if (!response.ok) {
83
+ const text = await response.text().catch(() => '');
84
+ throw new PenpotApiError(response.status, text.slice(0, 300).trim());
85
+ }
86
+ const raw = await response.json();
87
+ return { id: raw['id'] === undefined ? undefined : String(raw['id']), raw };
88
+ },
89
+ };
90
+ }
91
+ //# 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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"}
@@ -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(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,285 @@
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.',
164
+ parameters: {
165
+ query: { type: 'string', required: true, description: 'Search term.' },
166
+ },
167
+ output: {
168
+ schema: {
169
+ type: 'object',
170
+ additionalProperties: false,
171
+ properties: {
172
+ backend: { type: 'string', required: true, description: 'Which backend answered: api.' },
173
+ items: {
174
+ type: 'array',
175
+ required: true,
176
+ description: 'Penpot objects, id and name only.',
177
+ items: {
178
+ type: 'object',
179
+ additionalProperties: false,
180
+ properties: {
181
+ id: { type: 'string', required: true, description: 'Penpot uuid.' },
182
+ name: { type: 'string', required: true, description: 'Display name.' },
183
+ },
184
+ },
185
+ },
186
+ },
187
+ },
188
+ render: (_args, value) => [{ type: 'text', text: describeRefs(value) }],
189
+ },
190
+ async execute(args) {
191
+ return { backend: requireBackend().kind, items: await requireBackend().searchFiles(args.query) };
192
+ },
193
+ }));
194
+ ctx.tools.register(defineTool({
195
+ name: 'penpot_file',
196
+ description: 'Show one design file: its id, name and pages. Use it to find the page ids an export or a future write needs.',
197
+ parameters: {
198
+ fileId: { type: 'string', required: true, description: 'File uuid from penpot_files or penpot_search.' },
199
+ },
200
+ output: {
201
+ schema: {
202
+ type: 'object',
203
+ additionalProperties: false,
204
+ properties: {
205
+ backend: { type: 'string', required: true, description: 'api, or none when not configured.' },
206
+ file: {
207
+ type: 'object',
208
+ required: true,
209
+ additionalProperties: false,
210
+ properties: {
211
+ id: { type: 'string', required: true, description: 'File uuid.' },
212
+ name: { type: 'string', required: true, description: 'File name.' },
213
+ },
214
+ },
215
+ },
216
+ },
217
+ render: (_args, value) => [{ type: 'text', text: `${value.file.id}\t${value.file.name} (${value.backend})` }],
218
+ },
219
+ async execute(args) {
220
+ const summary = await requireBackend().fileSummary(args.fileId);
221
+ return { backend: requireBackend().kind, file: { id: summary.id, name: summary.name } };
222
+ },
223
+ }));
224
+ ctx.tools.register(defineTool({
225
+ name: 'penpot_export',
226
+ 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.',
227
+ parameters: {
228
+ fileId: { type: 'string', required: true, description: 'File uuid.' },
229
+ path: { type: 'string', required: true, description: 'Absolute path to write.' },
230
+ },
231
+ output: {
232
+ schema: {
233
+ type: 'object',
234
+ additionalProperties: false,
235
+ properties: {
236
+ path: { type: 'string', required: true, description: 'Where the file was written.' },
237
+ bytes: { type: 'number', required: true, description: 'Size in bytes.' },
238
+ },
239
+ },
240
+ render: (_args, value) => [{ type: 'text', text: `${value.path} (${String(value.bytes)} bytes)` }],
241
+ },
242
+ async execute(args) {
243
+ const bytes = await requireBackend().exportFile(args.fileId);
244
+ const { writeFile } = await import('node:fs/promises');
245
+ await writeFile(args.path, bytes);
246
+ return { path: args.path, bytes: bytes.byteLength };
247
+ },
248
+ }));
249
+ ctx.tools.register(defineTool({
250
+ name: 'penpot_import',
251
+ 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.',
252
+ parameters: {
253
+ path: { type: 'string', required: true, description: 'Absolute path of the .penpot file to upload.' },
254
+ projectId: { type: 'string', description: 'Target project uuid from penpot_projects. Required unless fileId is given.' },
255
+ fileId: { type: 'string', description: 'Existing file uuid to replace. Omit to create a new file.' },
256
+ name: { type: 'string', description: 'Display name for a new file. Ignored when fileId is given.' },
257
+ },
258
+ output: {
259
+ schema: {
260
+ type: 'object',
261
+ additionalProperties: false,
262
+ properties: {
263
+ id: { type: 'string', required: true, description: 'Penpot uuid of the imported file.' },
264
+ },
265
+ },
266
+ render: (_args, value) => [{ type: 'text', text: `imported ${value.id}` }],
267
+ },
268
+ async execute(args) {
269
+ if ((args.projectId === undefined || args.projectId.length === 0)
270
+ && (args.fileId === undefined || args.fileId.length === 0)) {
271
+ throw new Error('penpot_import needs a projectId for a new file, or a fileId to replace one');
272
+ }
273
+ const result = await requireBackend().importFile({
274
+ path: args.path,
275
+ ...(args.projectId === undefined ? {} : { projectId: args.projectId }),
276
+ ...(args.fileId === undefined ? {} : { fileId: args.fileId }),
277
+ ...(args.name === undefined ? {} : { name: args.name }),
278
+ });
279
+ // Penpot's import answers the new file id. A backend that cannot report
280
+ // one has still done the import, so name what is known rather than lie.
281
+ return { id: result.id ?? 'unknown (Penpot did not return a file id)' };
282
+ },
283
+ }));
284
+ }
285
+ //# sourceMappingURL=index.js.map
@@ -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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
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
+ }