Implement the penpot_whoami tool and wire the extension entry point #189

Closed
opened 2026-09-14 23:08:50 +00:00 by david · 1 comment
Owner

Summary

Implement the penpot_whoami tool and wire it into the extension entry point — the first callable penpot tool. It returns the authenticated user's profile, proves the auth path end to end, and gives a clean, actionable failure when PENPOT_URL/PENPOT_TOKEN are missing or wrong.

Background

Depends on: #188

This is the milestone's proof-of-life step: a single no-parameter command (get-profile) called through the shared client. On Penpot 2.17, get-profile and get-teams need no parameters — calling them is the cheapest way to confirm the token, the base URL and the header scheme all work.

This step also establishes the tool-registration pattern every later penpot tool copies:

  • one pi.registerTool({...}) call per tool, kept in a module that exports the tool definition, with extensions/penpot/src/index.ts doing the registration (see extensions/victorialogs/index.ts for the exact shape);
  • a TypeBox parameter schema (here: no parameters, so an empty object schema with additionalProperties: false if the API supports it);
  • a tool description written for the model, mentioning that a missing/expired token is the common failure;
  • errors returned as readable text in the tool result, never thrown as unhandled exceptions.

Documentation Required

A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting.

docs/reference/pi-coding-agent/

docs/reference/typebox/

  • https://github.com/sinclairzx81/typeboxType.Object / Type.Optional and static type inference; the parameter schema for this tool is empty, but the pattern must match the sibling extensions.

docs/reference/penpot-api/

  • https://help.penpot.app/technical-guide/integration/ — the canonical get-profile curl example: confirms the command name, the GET verb and the Authorization: Token <token> header.
  • <PENPOT_URL>/api/main/doc/openapi.json — confirm get-profile takes no required parameters on this instance before finalising the empty schema.

Implementation Details

  1. extensions/penpot/src/tools/whoami.ts (create a src/tools/ directory — later milestones add frame.ts, rect.ts, text.ts, svg.ts, library.ts, commit.ts, etc. there):

    • Export the tool definition object; call penpotRequest("get-profile") through the shared client.
    • On success, format a compact human-readable summary (name, email, id) plus a details object with the raw profile fields — do not dump the entire profile blob into the text channel.
    • On failure, return the categorised error text from src/errors.ts. For a config error, the message must tell the user exactly what to set (PENPOT_URL without /api, PENPOT_TOKEN from Penpot account settings).
    • The tool takes no arguments; do not accept a URL/token override (every penpot tool reads the same environment config).
  2. extensions/penpot/src/index.ts — register the tool:

    import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
    import { whoamiTool } from "./tools/whoami.ts";
    
    export default function (pi: ExtensionAPI): void {
      pi.registerTool(whoamiTool);
    }
    

    Keep everything lazy: no resolveConfig() call and no network access at load time.

  3. extensions/penpot/README.md — add a short "Tools" section listing penpot_whoami and a "Configuration" section documenting PENPOT_URL (base URL, no /api) and PENPOT_TOKEN. The full reference (all tools, safety model) is completed in a later milestone; do not write placeholder tables for tools that do not exist yet.

Write a co-located unit test for the tool's success and failure formatting with a stubbed client.

Acceptance Criteria

  • penpot_whoami is registered by extensions/penpot/src/index.ts and callable in pi.
  • The tool takes no parameters and calls the get-profile command through src/client.ts.
  • With valid credentials it returns the profile (name/email/id) and does not flood the result with the raw payload.
  • With a missing PENPOT_URL or PENPOT_TOKEN, the tool returns an actionable config message naming the variable and its expected value — no stack trace.
  • With an invalid token, the tool returns a human-readable auth message (no raw Transit dump).
  • No network request or env read happens at extension load time.
  • extensions/penpot/README.md documents both environment variables.
  • Unit tests for the tool are green with no network access.

Test Plan

  1. Unit tests (stubbed client, no credentials):

    node --test extensions/penpot/src/tools/whoami.test.ts
    
  2. Live validation against the scratch project's instance (requires PENPOT_URL/PENPOT_TOKEN):

    • In pi, /reload, then call penpot_whoami → expect the token owner's name/email.
    • Unset PENPOT_TOKEN and call again → expect an actionable config message, not a crash.
    • Set PENPOT_TOKEN to a deliberately invalid value and call again → expect a readable auth error containing no raw Transit payload.
  3. Record the outcome (pass/fail per case) in the milestone validation issue — this milestone does not advance on an unvalidated auth path.

## Summary Implement the `penpot_whoami` tool and wire it into the extension entry point — the first callable penpot tool. It returns the authenticated user's profile, proves the auth path end to end, and gives a clean, actionable failure when `PENPOT_URL`/`PENPOT_TOKEN` are missing or wrong. ## Background **Depends on:** #188 This is the milestone's proof-of-life step: a single no-parameter command (`get-profile`) called through the shared client. On Penpot 2.17, `get-profile` and `get-teams` need no parameters — calling them is the cheapest way to confirm the token, the base URL and the header scheme all work. This step also establishes the tool-registration pattern every later penpot tool copies: - one `pi.registerTool({...})` call per tool, kept in a module that exports the tool definition, with `extensions/penpot/src/index.ts` doing the registration (see `extensions/victorialogs/index.ts` for the exact shape); - a TypeBox parameter schema (here: no parameters, so an empty object schema with `additionalProperties: false` if the API supports it); - a tool description written for the model, mentioning that a missing/expired token is the common failure; - errors returned as readable text in the tool result, never thrown as unhandled exceptions. ## Documentation Required A separate process downloads these into the listed folders before this issue is implemented. Check the folders for the actual reference material before starting. **`docs/reference/pi-coding-agent/`** - https://pi.dev/docs/latest/extensions — `pi.registerTool` signature: the `name`, `description`, `parameters` and `execute` fields, and how tool results are returned to the model. - https://pi.dev/docs/latest/skills — adjacent context on how skills advertise extension tools (the skill that consumes this tool family is written in a later milestone). **`docs/reference/typebox/`** - https://github.com/sinclairzx81/typebox — `Type.Object` / `Type.Optional` and static type inference; the parameter schema for this tool is empty, but the pattern must match the sibling extensions. **`docs/reference/penpot-api/`** - https://help.penpot.app/technical-guide/integration/ — the canonical `get-profile` curl example: confirms the command name, the `GET` verb and the `Authorization: Token <token>` header. - `<PENPOT_URL>/api/main/doc/openapi.json` — confirm `get-profile` takes no required parameters on this instance before finalising the empty schema. ## Implementation Details 1. **`extensions/penpot/src/tools/whoami.ts`** (create a `src/tools/` directory — later milestones add `frame.ts`, `rect.ts`, `text.ts`, `svg.ts`, `library.ts`, `commit.ts`, etc. there): - Export the tool definition object; call `penpotRequest("get-profile")` through the shared client. - On success, format a compact human-readable summary (name, email, id) plus a `details` object with the raw profile fields — do not dump the entire profile blob into the text channel. - On failure, return the categorised error text from `src/errors.ts`. For a `config` error, the message must tell the user exactly what to set (`PENPOT_URL` without `/api`, `PENPOT_TOKEN` from Penpot account settings). - The tool takes no arguments; do not accept a URL/token override (every penpot tool reads the same environment config). 2. **`extensions/penpot/src/index.ts`** — register the tool: ```ts import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { whoamiTool } from "./tools/whoami.ts"; export default function (pi: ExtensionAPI): void { pi.registerTool(whoamiTool); } ``` Keep everything lazy: no `resolveConfig()` call and no network access at load time. 3. **`extensions/penpot/README.md`** — add a short "Tools" section listing `penpot_whoami` and a "Configuration" section documenting `PENPOT_URL` (base URL, no `/api`) and `PENPOT_TOKEN`. The full reference (all tools, safety model) is completed in a later milestone; do not write placeholder tables for tools that do not exist yet. Write a co-located unit test for the tool's success and failure formatting with a stubbed client. ## Acceptance Criteria - [ ] `penpot_whoami` is registered by `extensions/penpot/src/index.ts` and callable in pi. - [ ] The tool takes no parameters and calls the `get-profile` command through `src/client.ts`. - [ ] With valid credentials it returns the profile (name/email/id) and does not flood the result with the raw payload. - [ ] With a missing `PENPOT_URL` or `PENPOT_TOKEN`, the tool returns an actionable config message naming the variable and its expected value — no stack trace. - [ ] With an invalid token, the tool returns a human-readable auth message (no raw Transit dump). - [ ] No network request or env read happens at extension load time. - [ ] `extensions/penpot/README.md` documents both environment variables. - [ ] Unit tests for the tool are green with no network access. ## Test Plan 1. Unit tests (stubbed client, no credentials): ```bash node --test extensions/penpot/src/tools/whoami.test.ts ``` 2. Live validation against the scratch project's instance (requires `PENPOT_URL`/`PENPOT_TOKEN`): - In pi, `/reload`, then call `penpot_whoami` → expect the token owner's name/email. - Unset `PENPOT_TOKEN` and call again → expect an actionable config message, not a crash. - Set `PENPOT_TOKEN` to a deliberately invalid value and call again → expect a readable auth error containing no raw Transit payload. 3. Record the outcome (pass/fail per case) in the milestone validation issue — this milestone does not advance on an unvalidated auth path.
david closed this issue 2026-09-14 23:47:29 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #222

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/222
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills#189
No description provided.