papercliped 1.1.0-beta.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +14 -11
- package/.claude-plugin/plugin.json +17 -8
- package/.mcp.json +8 -14
- package/CHANGELOG.md +64 -1
- package/LICENSE +21 -21
- package/README.md +80 -80
- package/commands/paperclip-report.md +5 -5
- package/commands/paperclip-status.md +4 -4
- package/commands/paperclip-sync.md +5 -5
- package/dist/accounts/alias.js +1 -1
- package/dist/accounts/username.js +1 -1
- package/dist/cli.js +11 -11
- package/dist/http.js +17 -3
- package/dist/manage/routes.js +60 -82
- package/dist/oauth/pages.js +28 -18
- package/dist/oauth/pg-store.js +0 -13
- package/dist/oauth/provider.js +27 -63
- package/dist/oauth/scopes.js +19 -16
- package/dist/openapi.js +19 -2
- package/dist/public-api/health.js +46 -0
- package/dist/public-api/openapi.js +51 -0
- package/dist/public-api/routes.js +251 -0
- package/dist/public-api/types.js +14 -0
- package/dist/server.js +4 -0
- package/dist/site/landing.js +3 -3
- package/dist/site/markdown.js +2 -1
- package/dist/site/site.js +16 -4
- package/dist/telemetry/recorder.js +1 -1
- package/dist/tools.js +48 -2
- package/dist/web/csp.js +16 -0
- package/dist/web/hosts.js +23 -0
- package/dist/web/routes.js +132 -0
- package/docs/ARCHITECTURE.md +74 -73
- package/docs/COMMUNITY.md +86 -0
- package/docs/DEPLOY-RENDER.md +3 -3
- package/docs/DOMAINS.md +74 -0
- package/docs/LAUNCH.md +50 -34
- package/docs/RELEASING.md +37 -0
- package/docs/SECURITY.md +82 -65
- package/docs/SITEMAP.md +92 -0
- package/docs/V3-SUBDOMAINS.md +106 -0
- package/docs/chatgpt.md +20 -20
- package/docs/least-privilege.sql +23 -23
- package/docs/legal/PRIVACY.md +37 -37
- package/docs/legal/TERMS.md +11 -11
- package/docs/oauth.md +61 -62
- package/docs/supabase-schema.sql +8 -1
- package/migrations/001_init.sql +84 -84
- package/migrations/002_audit.sql +41 -41
- package/migrations/003_accounts.sql +66 -66
- package/migrations/004_privacy_panel.sql +57 -57
- package/migrations/006_two_levels.sql +5 -0
- package/package.json +10 -14
- package/site/docs/chatgpt.md +1 -1
- package/site/docs/cli.md +27 -0
- package/site/docs/connect-your-paperclip.md +120 -0
- package/site/docs/environment.md +64 -0
- package/site/docs/faq.md +31 -0
- package/site/docs/getting-started.md +11 -5
- package/site/docs/limits-and-errors.md +32 -0
- package/site/docs/manage-api.md +33 -0
- package/site/docs/manage.md +12 -21
- package/site/docs/other-ai-apps.md +28 -0
- package/site/docs/paperclip-plugin.md +32 -0
- package/site/docs/permissions.md +5 -5
- package/site/docs/public-api.md +62 -0
- package/site/docs/security.md +2 -2
- package/site/docs/self-hosting.md +43 -0
- package/site/docs/signup.md +3 -3
- package/site/docs/tools.md +47 -0
- package/site/docs/troubleshooting.md +2 -2
- package/site/docs/what-is-papercliped.md +37 -0
- package/site/privacy.md +5 -3
- package/site/terms.md +1 -1
- package/skills/paperclip/SKILL.md +23 -23
- package/dist/demo.js +0 -144
- package/dist/manage/page.js +0 -170
- package/dist/panel/config.js +0 -61
- package/dist/panel/main.js +0 -90
- package/dist/panel/page.js +0 -553
- package/dist/panel/routes.js +0 -284
- package/dist/panel/server.js +0 -37
- package/docs/panel-role.sql +0 -26
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "papercliped",
|
|
3
|
-
"owner": {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "papercliped",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "KpnWorld"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "papercliped",
|
|
9
|
+
"source": "./",
|
|
10
|
+
"description": "Run your Paperclip from Claude: control agents, sync with them and get reports, at the access level you choose."
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"description": "Papercliped: run your Paperclip from Claude Code."
|
|
14
|
+
}
|
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "papercliped",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "
|
|
5
|
-
"author": {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "papercliped",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Run your Paperclip from Claude: control agents, sync with them and get reports, at the access level you choose.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "KpnWorld"
|
|
7
|
+
},
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"paperclip",
|
|
11
|
+
"agents",
|
|
12
|
+
"orchestration",
|
|
13
|
+
"mcp"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://papercliped.co",
|
|
16
|
+
"repository": "https://github.com/OpenSourcx/papercliped"
|
|
17
|
+
}
|
package/.mcp.json
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
{
|
|
2
|
-
"mcpServers": {
|
|
3
|
-
"paperclip": {
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"PAPERCLIP_COMPANY_ID": "${PAPERCLIP_COMPANY_ID:-}",
|
|
10
|
-
"PAPERCLIP_READ_ONLY": "${PAPERCLIP_READ_ONLY:-}"
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"paperclip": {
|
|
4
|
+
"type": "http",
|
|
5
|
+
"url": "https://mcp.papercliped.co/mcp"
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## v2.1.0 — two access levels, manage in Paperclip, new website
|
|
6
|
+
|
|
7
|
+
- **Dependencies up to date:** Zod 4 (tool schemas and the ChatGPT Actions OpenAPI now come from Zod's built-in JSON Schema; the published contract is unchanged), the latest MCP SDK, undici 8, TypeScript 7, Vitest 5, React 19 and esbuild 0.28 in the plugin, and GitHub Actions v7. No known vulnerabilities in either package. Dependabot now also watches the website and groups minor and patch updates into one pull request per package.
|
|
8
|
+
|
|
9
|
+
- **Install in one step from Claude Code and Codex.** Both plugin marketplaces live in this repository: `/plugin marketplace add OpenSourcx/papercliped` in Claude Code, `codex plugin marketplace add OpenSourcx/papercliped` in Codex. The plugin now connects to the hosted server (`https://mcp.papercliped.co/mcp`) instead of a local build that wasn't in the repository; people running Paperclip on their own machine use `npx papercliped@latest` (see the README).
|
|
10
|
+
- **A README that gets you running:** what Papercliped is and copy-paste setup for Claude, Claude Code, Codex, ChatGPT, any MCP app and local Paperclips, without visiting the docs.
|
|
11
|
+
- **Every push is a release.** CI publishes both npm packages, tags the commit and writes the GitHub release from the changelog and the commit descriptions. `scripts/version.mjs` bumps Major.Minor.Patch in every file, and a release skill for Claude and Codex (`.claude/skills/release`, `.agents/skills/release`) explains when to use which, and how to adopt the same flow in other repositories.
|
|
12
|
+
- **Ready for contributors:** `SECURITY.md` (private reporting, response times), a contributor guide with the path from first PR to maintainer, `CLAUDE.md`/`AGENTS.md` for AI coding agents, and a complete Discord setup in `docs/COMMUNITY.md`.
|
|
13
|
+
|
|
14
|
+
- **New website, phase 1 (foundation):** `web/`, a static React + Tailwind site with self-hosted fonts (Bricolage Grotesque, Inter, JetBrains Mono), the mascot as an animated component (eyes follow the pointer, blinks; still with reduced motion), a component kit and a `/kit` showcase page. **Seasonal themes:** the default `clip` theme plus two palettes per season, each with light and dark, rotating on a schedule that is the same for everyone on a given day; visitors can pick a theme and light/dark/system mode. Every theme passes WCAG AA contrast (tested), every route is checked in headless Chromium under the strict CSP, and nothing is loaded from third parties. Not yet served by the bridge (phase 4).
|
|
15
|
+
- **New website, phase 2 (pages):** a full landing page (live user count, an interactive how-it-works stepper, a permission playground generated from the real tool list, the two plugins, security highlights, FAQ), a **changelog page** built from this file with New/Improved/Fixed filters and an Atom feed (`/changelog.xml`), a **community page** (a suggest-a-feature form that pre-fills a GitHub issue, bug reports, contributing; social links only appear once they exist), a **brand page** with mascot and wordmark downloads and usage rules, and privacy and terms rendered from the same Markdown as the bridge. Playful but accessible interactions (lifting buttons, tilting cards, a mascot that hops), checked for sideways scrolling on a 375px phone. Also: `CODE_OF_CONDUCT.md`, issue and pull request templates, `docs/COMMUNITY.md` (forum and Discord plan), sitemap, robots.txt and a social preview image.
|
|
16
|
+
|
|
17
|
+
- **Public stats API** (no sign-in): `GET /api/public/v1/info`, `status`, `stats`, `series` and `errors` (plus `openapi.json`) publish aggregate users, live connections, sign-in success and failure reasons, request success rate, latency percentiles, error mix and load. Aggregate only: no usernames, aliases, ids, Paperclip addresses, app names or IPs, and no per-user breakdown (tested against memory and Postgres). GET-only, CORS-open without credentials, cached 30 s, 60 requests per minute per address. Types in `src/public-api/types.ts`. New docs page: **Public API and status**.
|
|
18
|
+
- **Status page** on the new website (`/status`): live status badge, people, connections, request and sign-in success, latency percentiles, and charts for requests and errors, response time and sign-ins, plus error-mix, sign-in-failure and top-tool bars. Every chart has a text summary and a table view and never relies on colour alone; it refreshes every 30 seconds.
|
|
19
|
+
- **Removed the operator panel from this repository** (`papercliped-panel`, `src/panel/`, the demo, `PANEL_*` settings, `docs/panel-role.sql`). The operator dashboard now lives in a separate, access-controlled service. Nothing changed in the database: the telemetry tables and the read-only `panel_*` views are still written and kept for it. `npm run build` now cleans `dist/` first.
|
|
20
|
+
|
|
21
|
+
- **New website, phase 3 (docs):** a docs site at `/docs` with a three-column layout (collapsible sections with counts, the article with breadcrumb, last-updated date, copy link and edit-on-GitHub, and an "On this page" outline that follows your scroll), previous/next links, copy buttons on code, and **Ctrl/Cmd-K search** built at compile time (no outside service). The **GitHub star count** shows in the header: the bridge fetches it from GitHub and caches it for an hour (`GET /api/public/v1/repo`), so browsers never call GitHub directly.
|
|
22
|
+
- **Much more documentation:** new pages What is Papercliped? (with a glossary), Run it yourself, Tool reference (generated from the code), Manage API, Environment variables, Command line, Limits and errors, and FAQ. Docs now have 20 pages in five sections; the bridge serves the same pages until the new site goes live.
|
|
23
|
+
|
|
24
|
+
- **The bridge serves the new website** (from `web/dist`, built into the Docker image) with **host routing** (`PUBLIC_HOSTS`): `papercliped.co` for the site, `docs.papercliped.co/topics` for the docs, `mcp.`/`api.papercliped.co` for MCP, OAuth and the APIs, `forum.` redirects to the forum, `www` redirects to the apex. Old `/docs/...` links redirect to the docs host. Every host still answers the protocol and API paths. Unknown pages return a real 404. Without the built site (e.g. the npm package), the bridge serves its Markdown pages as before. Setup and the issuer decision: `docs/DOMAINS.md`.
|
|
25
|
+
- **Sign-in, consent and manage pages restyled** with the website's fonts (Bricolage Grotesque, Inter, JetBrains Mono) and colours, including dark mode. Still plain server-rendered HTML.
|
|
26
|
+
- **New tool `papercliped_service_status`** (read only): the AI can check whether the hosted service is working, from the public status API. The catalogue is now 30 tools.
|
|
27
|
+
- **Paperclip plugin:** a "Papercliped service" card shows the bridge's public status (no link needed).
|
|
28
|
+
|
|
29
|
+
- **AI apps now connect at `https://mcp.papercliped.co/mcp`** (the OAuth issuer moves to `mcp.papercliped.co`; the website, docs and `/manage` stay on `papercliped.co`). Docs show the connector address through a new `{{MCP}}` placeholder, so self-hosted bridges show their own. Anyone connected before must remove the connector and add the new address.
|
|
30
|
+
|
|
31
|
+
- **Website redesign:** a bolder homepage (a centred headline, pill buttons, a gradient pill hero with the mascot in the middle; click it for a wave), a shorter header (Product, Docs, Resources, Community, search, GitHub stars, Get started). The docs get their own header (links left, "papercliped Docs" centred, search and stars right), a sidebar grouped into Learn, Reference and Project with icons, counts and collapsible sections, and a page header with breadcrumb, tag and a Copy link menu (view the Markdown source, suggest an edit). The changelog moves into the docs layout as expandable release cards.
|
|
32
|
+
- **Light and dark now follow your device automatically.** The manual System/Light/Dark switch is gone (an old saved choice is cleared); the seasonal palettes still rotate on their own.
|
|
33
|
+
|
|
34
|
+
- **Two access levels: Read only and Full control.** Full control is now every tool (pause, wake, terminate, issues, goals, comments, approvals, budgets and raw API writes), so you can run your Paperclip from anywhere. The separate "Admin" level is gone; existing admin grants and apps that still ask for it get Full control (migration `006_two_levels.sql` tidies stored grants). The sign-in page, the manage page, the Paperclip plugin, the docs and the website playground all show the two levels.
|
|
35
|
+
|
|
36
|
+
- **Managing your account moved into Paperclip.** The Papercliped plugin is now the connection manager: link it with your username and secret key (checked once, not stored), then change levels, disconnect apps, switch anonymity, see and remove linked Paperclips, make a new secret key (this Paperclip stays linked), disconnect your Paperclip or delete your account. The `/manage` web page, its cookie sign-in, one-time link codes and the beta opt-in are gone (`/manage` now points to the plugin docs); the manage API accepts plugin tokens only.
|
|
37
|
+
|
|
38
|
+
- **Docs live at `docs.papercliped.co/<page>`** (for example `docs.papercliped.co/permissions`), with a real docs landing at `docs.papercliped.co/`: search, the connector address, four start-here cards and every topic. Old `/topics/...` and `papercliped.co/docs/...` links redirect.
|
|
39
|
+
|
|
40
|
+
- **A cleaner homepage hero:** instead of decorative art, a live demo of the product. An AI app on the left steers a Paperclip on the right (pause an agent, weekly report, approve a request), using the real tool names, with tabs to replay each one. The footer gets X, GitHub and Discord icons (X and Discord point to the community page until their addresses are set).
|
|
41
|
+
|
|
42
|
+
- **The Papercliped page in Paperclip, rebuilt** with Paperclip's own components (status badges, metric cards, tables, toasts) so it looks native: a service panel, an account summary, and tabs for Connected apps (level dropdown, two-step disconnect), Privacy, Linked Paperclips and Account (new secret key shown once with copy, disconnect Paperclip, delete account). Covered by UI tests against the real plugin actions.
|
|
43
|
+
|
|
44
|
+
## v2.0.0 — stable open-source build
|
|
45
|
+
|
|
46
|
+
Papercliped v2 is the free, open-source foundation: the bridge, the connector for Claude and any MCP app, and the Paperclip plugin. Paid features (managed subdomains, pricing, legal) are planned for v3 (`docs/V3-SUBDOMAINS.md`).
|
|
47
|
+
|
|
48
|
+
- **New home: `https://papercliped.co`.** Every reference to the old domain is gone. The Paperclip plugin's default bridge URL is now `https://papercliped.co`. Operators: set `BRIDGE_PUBLIC_URL=https://papercliped.co`; apps connected under the old address must connect again (the OAuth issuer changed). Cloudflare + Render setup: `docs/LAUNCH.md` §3.
|
|
49
|
+
- **Two plugins, documented side by side:** Papercliped for AI apps (Claude, ChatGPT, any MCP client) and the Papercliped plugin for Paperclip. New docs page: **Any AI app (MCP)**.
|
|
50
|
+
- **Automated npm publishing** through GitHub Actions with npm trusted publishing (OIDC, no token): one tag publishes `papercliped` and `papercliped-paperclip-plugin` and creates the GitHub release (`docs/RELEASING.md`). CI now also packs both packages and installs them as a user would, and a test keeps every version string in step.
|
|
51
|
+
- **Foundation:** CodeQL, Dependabot, dependency audit and read-only permissions in CI; `CONTRIBUTING.md`; `docs/SITEMAP.md` (every page and endpoint); new docs page **Set up your Paperclip** (public address with or without a domain); the security doc covers plugin links.
|
|
52
|
+
- Fixed: the Claude Code install command in the README (`/plugin install papercliped@papercliped`).
|
|
53
|
+
|
|
54
|
+
Not tested live: the Paperclip plugin has never run inside a real Paperclip, and the release workflow has not run yet (it can only run on GitHub).
|
|
55
|
+
|
|
56
|
+
## v1.2.0-beta.1 — Paperclip plugin
|
|
57
|
+
|
|
58
|
+
- **Paperclip plugin** (`plugin/`, npm `papercliped-paperclip-plugin`, a separate package built on `@paperclipai/plugin-sdk`): a **Papercliped** sidebar entry and page inside Paperclip to link your account, list connected apps, switch them between Read only and Full control (beta), disconnect them, toggle anonymity, and unlink. The bridge URL is configurable (https only).
|
|
59
|
+
- **Plugin link API**: `POST /api/manage/plugin-link` (beta, browser session) makes a one-time `pcl_…` code (10 minutes, single use); `POST /api/manage/plugin-link/exchange` trades it for a long-lived `pcb_pl_…` token (rate-limited by address; wrong, used and expired codes answer identically); `GET /api/manage/plugin-links`; `DELETE /api/manage/plugin-links/:id`. The `/manage` page gains a **Link Paperclip plugin** section.
|
|
60
|
+
- **Plugin tokens** are accepted as `Authorization: Bearer` on the manage API (no CSRF header needed). They hold no scopes, so they cannot call any Paperclip tool or the MCP endpoint, and they cannot mint codes, change the beta, sign out, or reach any action that needs the secret key (rotate key, disconnect Paperclip, delete account); those return 403 and need the browser. A token can remove only itself.
|
|
61
|
+
- **Making a new secret key now also revokes every plugin link.**
|
|
62
|
+
- New docs page: Paperclip plugin.
|
|
63
|
+
- New docs page: **Set up your Paperclip**, a step-by-step guide to getting a public https address (Cloudflare Tunnel with your own domain, or Tailscale Funnel, ngrok or a Cloudflare quick tunnel without one).
|
|
64
|
+
|
|
65
|
+
Not tested: the plugin has never run inside a live Paperclip. It is covered by unit tests against a fake bridge and the SDK's test harness only.
|
|
66
|
+
|
|
3
67
|
## v1.1.0-beta.2 — connection manager, public site
|
|
4
68
|
|
|
5
69
|
- **Beta connection manager** at `/manage` and a management API (`/api/manage/*`): sign in with username + secret key, see every connected app, switch it between Read only and Full control (beta) with immediate effect, disconnect it, toggle anonymity, make a new secret key, disconnect your Paperclip, delete your account. Sensitive actions re-ask for the secret key; Admin is never grantable from here. Opt in with **Join the beta** on the permission screen (migration `005_beta.sql`, applied automatically).
|
|
@@ -8,7 +72,6 @@
|
|
|
8
72
|
- Windows-friendly test suite; `npm publish` only builds.
|
|
9
73
|
- Set `SITE_CONTACT` (an email or support URL) so the privacy and terms pages show a real contact.
|
|
10
74
|
|
|
11
|
-
Not in this release: a native Paperclip plugin that embeds the manager inside Paperclip (it needs the Paperclip plugin SDK).
|
|
12
75
|
|
|
13
76
|
## v1.0.0-beta.1 — first public beta
|
|
14
77
|
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 KpnWorld
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KpnWorld
|
|
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,122 +1,122 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Papercliped
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Run your [Paperclip](https://github.com/paperclipai/paperclip) from any AI app.** Papercliped connects Claude, ChatGPT, Codex and any MCP app to your Paperclip, the open-source orchestrator for companies of AI agents. Check on agents, pause or wake them, assign issues, decide approvals and get status, cost and activity reports, without opening the dashboard.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
| Sync with agents | `paperclip_sync_snapshot` (state now), `paperclip_sync_changes` (cursor-based "what changed"), `paperclip_wait_for_agent` (follow a run to completion) |
|
|
9
|
-
| Reports | status, costs, agent performance, activity digest — markdown for people plus structured data |
|
|
10
|
-
| Use the API | every tool is a thin call to Paperclip's `/api`; `paperclip_api_request` reaches anything without a dedicated tool |
|
|
5
|
+
[](https://github.com/OpenSourcx/papercliped/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/papercliped)
|
|
7
|
+
[](LICENSE)
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
Free and open source. Website: [papercliped.co](https://papercliped.co) · Docs: [docs.papercliped.co](https://docs.papercliped.co)
|
|
10
|
+
|
|
11
|
+
> Papercliped is an independent community project, not made by the Paperclip team.
|
|
12
|
+
|
|
13
|
+
## Add it to your AI app
|
|
14
|
+
|
|
15
|
+
You need a Paperclip with a public `https` address ([no domain? see this](https://docs.papercliped.co/connect-your-paperclip)). The first time you connect, you approve the request inside your own Paperclip, pick a username, save the secret key you're shown once, and choose **Read only** or **Full control**.
|
|
16
|
+
|
|
17
|
+
The connector address for every app is:
|
|
13
18
|
|
|
14
19
|
```
|
|
15
|
-
|
|
16
|
-
Any MCP client ──Streamable HTTP /mcp─▶ ├─ tools ─▶ Paperclip /api
|
|
17
|
-
ChatGPT Custom GPT ──/actions/* + /openapi.json─▶ ┘
|
|
20
|
+
https://mcp.papercliped.co/mcp
|
|
18
21
|
```
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
### Claude (web, desktop and mobile)
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
**Settings → Connectors → Add custom connector**, paste `https://mcp.papercliped.co/mcp`, then **Connect**.
|
|
26
|
+
|
|
27
|
+
### Claude Code
|
|
28
|
+
|
|
29
|
+
Install the plugin (adds the tools, a Paperclip skill and `/paperclip-status`, `/paperclip-sync`, `/paperclip-report`):
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
/plugin marketplace add OpenSourcx/papercliped
|
|
33
|
+
/plugin install papercliped@papercliped
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or just the MCP server:
|
|
23
37
|
|
|
24
38
|
```sh
|
|
25
|
-
|
|
26
|
-
npm install # also builds (prepare script)
|
|
27
|
-
npm test
|
|
39
|
+
claude mcp add --transport http paperclip https://mcp.papercliped.co/mcp
|
|
28
40
|
```
|
|
29
41
|
|
|
30
|
-
|
|
42
|
+
Then run `/mcp` to sign in.
|
|
43
|
+
|
|
44
|
+
### Codex
|
|
31
45
|
|
|
32
|
-
|
|
46
|
+
Install the plugin:
|
|
33
47
|
|
|
34
48
|
```sh
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export PAPERCLIP_API_KEY=<board token> # not needed in local_trusted mode
|
|
38
|
-
export PAPERCLIP_COMPANY_ID=<optional default company>
|
|
39
|
-
export PAPERCLIP_READ_ONLY=1 # optional: reports + sync only, no writes
|
|
49
|
+
codex plugin marketplace add OpenSourcx/papercliped
|
|
50
|
+
codex plugin add papercliped@papercliped
|
|
40
51
|
```
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
Or just the MCP server (Codex starts the sign-in for you):
|
|
43
54
|
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
/plugin install paperclip@papercliped
|
|
55
|
+
```sh
|
|
56
|
+
codex mcp add paperclip --url https://mcp.papercliped.co/mcp
|
|
47
57
|
```
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
The env vars above are read from your shell. Or without the plugin: `claude mcp add paperclip -- node /path/to/papercliped/dist/stdio.js`.
|
|
59
|
+
### ChatGPT
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
Turn on **Developer mode** (Settings → Apps → Advanced settings), choose **Create app**, paste `https://mcp.papercliped.co/mcp` and pick OAuth. Menu names change often; the [ChatGPT guide](https://docs.papercliped.co/chatgpt) has the current steps and the Custom GPT (Actions) option.
|
|
53
62
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"env": { "PAPERCLIP_API_URL": "http://localhost:3100", "PAPERCLIP_API_KEY": "…" } } } }
|
|
58
|
-
```
|
|
63
|
+
### Any other MCP app
|
|
64
|
+
|
|
65
|
+
Add a remote MCP server (Streamable HTTP, OAuth) at `https://mcp.papercliped.co/mcp`. See [Any AI app](https://docs.papercliped.co/other-ai-apps).
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
### Paperclip on your own machine (no public address)
|
|
61
68
|
|
|
62
|
-
Run
|
|
69
|
+
Run Papercliped locally instead. It talks straight to your Paperclip; nothing goes through papercliped.co. Use a Paperclip **board** token (agent keys can't pause or approve):
|
|
63
70
|
|
|
64
71
|
```sh
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
npm run start:http # 127.0.0.1:3939 — put TLS in front (Caddy, Cloudflare Tunnel, …)
|
|
68
|
-
```
|
|
72
|
+
# Claude Code
|
|
73
|
+
claude mcp add paperclip --env PAPERCLIP_API_URL=http://localhost:3100 --env PAPERCLIP_API_KEY=<board token> -- npx -y papercliped@latest
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
# Codex
|
|
76
|
+
codex mcp add paperclip --env PAPERCLIP_API_URL=http://localhost:3100 --env PAPERCLIP_API_KEY=<board token> -- npx -y papercliped@latest
|
|
77
|
+
```
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
Add `--env PAPERCLIP_READ_ONLY=1` for reports and sync only.
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
| --- | --- | --- | --- |
|
|
76
|
-
| **stdio** (`node dist/stdio.js`) | you, on your machine (Claude Code/Desktop) | your env vars | none |
|
|
77
|
-
| **HTTP + `BRIDGE_TOKEN`** | ChatGPT Actions, Claude Code headers, one instance | static bearer | none |
|
|
78
|
-
| **HTTP + OAuth, `BRIDGE_MODE=single`** | a team fronting *their one* Paperclip | OAuth 2.1, scoped | JSON file or Postgres |
|
|
79
|
-
| **HTTP + OAuth, `BRIDGE_MODE=multi`** | **a public service**: every user connects their own (publicly reachable, https) Paperclip | OAuth 2.1, scoped | Postgres (required) |
|
|
81
|
+
## What you can ask
|
|
80
82
|
|
|
81
|
-
|
|
83
|
+
- "What are my agents working on?" · "Which agents are over budget?"
|
|
84
|
+
- "Pause the Web Engineer." · "Wake the CEO and assign it the onboarding issue."
|
|
85
|
+
- "Approve the hire request." · "Weekly cost report, please."
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
There are 30 tools ([full list](https://docs.papercliped.co/tools)). **Read only** can look at agents, issues, goals, costs and reports. **Full control** can use every tool. The AI asks before anything that can't be undone, and you can change the level or disconnect at any time.
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
## Manage your connections in Paperclip
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
Install the **Papercliped plugin for Paperclip** (npm `papercliped-paperclip-plugin`, from **Settings → Plugins** in Paperclip). It adds a Papercliped page where you see every connected app, switch its level, disconnect it, go anonymous, and look after your account. [Plugin guide](https://docs.papercliped.co/paperclip-plugin).
|
|
88
92
|
|
|
89
|
-
##
|
|
93
|
+
## Security and privacy
|
|
90
94
|
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
95
|
+
- You approve every connection inside your own Paperclip; no passwords pass through Papercliped.
|
|
96
|
+
- Levels are enforced by Papercliped on every call, and every call is audit-logged.
|
|
97
|
+
- Paperclip keys are encrypted at rest; tokens and secret keys are stored only as one-way hashes.
|
|
98
|
+
- Only public `https` Paperclip addresses are reachable from the hosted service.
|
|
99
|
+
- No trackers or third-party scripts on the website.
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
Threat model: [docs/SECURITY.md](docs/SECURITY.md). Report a vulnerability: [SECURITY.md](SECURITY.md). Privacy: [papercliped.co/privacy](https://papercliped.co/privacy).
|
|
98
102
|
|
|
99
|
-
|
|
100
|
-
- **claude.ai web / Desktop / mobile connectors and ChatGPT MCP apps** need OAuth: set `BRIDGE_OAUTH=1` (see [docs/oauth.md](docs/oauth.md)). OAuth scopes limit *what* a connection may do, not *which company* it may touch (Paperclip board keys are user-wide; verified in its source). Not yet implemented: CIMD, a grant-admin web UI (use `npm run admin`).
|
|
101
|
-
- **A public bridge holds other people's Paperclip keys.** A bridge compromise exposes all of them until revoked. Read [docs/SECURITY.md](docs/SECURITY.md) before opening it up. The code is unaudited and the Render/Supabase/claude.ai/ChatGPT specifics are untested against the live services.
|
|
102
|
-
- **Hosted mode only reaches Paperclips on the public internet over https.** `localhost`/private-network instances use the local stdio plugin.
|
|
103
|
-
- `paperclip_sync_changes` polls the activity log (no push). Activity has no server-side `since` filter, so it fetches up to `limit` recent entries and filters locally; raise `limit` if you poll infrequently on a busy company.
|
|
104
|
-
- ChatGPT Actions allow ~30 operations per GPT; the catalogue is at 29. Adding tools means removing some for that surface.
|
|
103
|
+
## Run it yourself
|
|
105
104
|
|
|
106
|
-
|
|
105
|
+
The hosted service runs exactly this code. To host your own (for a team, or to keep everything on your infrastructure), see [Run it yourself](https://docs.papercliped.co/self-hosting), [docs/oauth.md](docs/oauth.md) and the [launch guide](docs/LAUNCH.md) (Render + Supabase, free tiers).
|
|
107
106
|
|
|
107
|
+
```sh
|
|
108
|
+
git clone https://github.com/OpenSourcx/papercliped && cd papercliped
|
|
109
|
+
npm install && npm test
|
|
108
110
|
```
|
|
109
|
-
src/tools.ts tool catalogue (schemas + Paperclip calls) src/reports.ts report builders
|
|
110
|
-
src/execute.ts validation, read-only enforcement, errors src/openapi.ts OpenAPI from the catalogue
|
|
111
|
-
src/mcp.ts MCP server src/stdio.ts src/server.ts + http.ts HTTP bridge
|
|
112
|
-
src/oauth/ OAuth 2.1 server: provider, stores (memory/JSON + Postgres), crypto/key ring, scopes, consent pages, Paperclip login adapter
|
|
113
|
-
src/telemetry/ audit events, batching recorder, system sampler, timing src/panel/ separate operator panel (routes, page, config, server)
|
|
114
|
-
src/net/ SSRF-safe fetch + instance URL validation (multi-tenant egress guard)
|
|
115
|
-
migrations/ Postgres schema (dedicated `bridge` schema, RLS) Dockerfile container image (Render free web service) src/cli.ts admin CLI
|
|
116
|
-
.claude-plugin/ .mcp.json skills/ commands/ Claude Code plugin
|
|
117
|
-
test/ end-to-end tests against a mock Paperclip
|
|
118
|
-
```
|
|
119
111
|
|
|
120
|
-
##
|
|
112
|
+
## Community and contributing
|
|
113
|
+
|
|
114
|
+
- Questions and ideas: [GitHub Discussions](https://github.com/OpenSourcx/papercliped/discussions) and Discord ([papercliped.co/community](https://papercliped.co/community))
|
|
115
|
+
- Bugs and feature requests: [issues](https://github.com/OpenSourcx/papercliped/issues/new/choose)
|
|
116
|
+
- Contributing (setup, checks, the path from first PR to maintainer): [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
117
|
+
- Code of conduct: [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
|
|
118
|
+
- Every push to `main` is a release: [changelog](CHANGELOG.md) · [how releases work](docs/RELEASING.md)
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
[MIT](LICENSE)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Produce a Paperclip report — status, costs, performance or activity
|
|
3
|
-
argument-hint: "[status|costs|performance|activity]"
|
|
4
|
-
---
|
|
5
|
-
Produce the report named in "$ARGUMENTS" (default `status`): `status` → `paperclip_report_status`, `costs` → `paperclip_report_costs`, `performance` → `paperclip_report_agent_performance`, `activity` → `paperclip_report_activity` (since the start of today, UTC). Present the markdown, then add 2–3 sentences of analysis: what looks off and what you would do about it.
|
|
1
|
+
---
|
|
2
|
+
description: Produce a Paperclip report — status, costs, performance or activity
|
|
3
|
+
argument-hint: "[status|costs|performance|activity]"
|
|
4
|
+
---
|
|
5
|
+
Produce the report named in "$ARGUMENTS" (default `status`): `status` → `paperclip_report_status`, `costs` → `paperclip_report_costs`, `performance` → `paperclip_report_agent_performance`, `activity` → `paperclip_report_activity` (since the start of today, UTC). Present the markdown, then add 2–3 sentences of analysis: what looks off and what you would do about it.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Show a Paperclip status report (agents, tasks, spend, approvals)
|
|
3
|
-
---
|
|
4
|
-
Call `paperclip_report_status` and present the result. Then list any agents in error or paused and any pending approvals, and suggest next steps — but do not take any action without asking.
|
|
1
|
+
---
|
|
2
|
+
description: Show a Paperclip status report (agents, tasks, spend, approvals)
|
|
3
|
+
---
|
|
4
|
+
Call `paperclip_report_status` and present the result. Then list any agents in error or paused and any pending approvals, and suggest next steps — but do not take any action without asking.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Sync with Paperclip agents — current state plus what changed since last sync
|
|
3
|
-
argument-hint: "[cursor from previous sync]"
|
|
4
|
-
---
|
|
5
|
-
Call `paperclip_sync_snapshot`. If a cursor was given ($ARGUMENTS), also call `paperclip_sync_changes` with it. Summarise what each agent is doing, what changed, and end with the new cursor so the next sync can continue from it.
|
|
1
|
+
---
|
|
2
|
+
description: Sync with Paperclip agents — current state plus what changed since last sync
|
|
3
|
+
argument-hint: "[cursor from previous sync]"
|
|
4
|
+
---
|
|
5
|
+
Call `paperclip_sync_snapshot`. If a cursor was given ($ARGUMENTS), also call `paperclip_sync_changes` with it. Summarise what each agent is doing, what changed, and end with the new cursor so the next sync can continue from it.
|
package/dist/accounts/alias.js
CHANGED
|
@@ -19,7 +19,7 @@ const POOL = [...new Set(NAMES)];
|
|
|
19
19
|
export const ALIAS_RE = /^[A-Z][a-z]{1,2}\d{2}$/;
|
|
20
20
|
export const generateAlias = (rand = (n) => randomInt(n)) => `${POOL[rand(POOL.length)]}${String(rand(100)).padStart(2, "0")}`;
|
|
21
21
|
export const isAlias = (s) => !!s && ALIAS_RE.test(s);
|
|
22
|
-
/** The name that appears in logs, the
|
|
22
|
+
/** The name that appears in logs, the operator dashboard and audit rows. */
|
|
23
23
|
export const displayName = (a) => (a.anonymous && a.alias ? a.alias : a.username);
|
|
24
24
|
/**
|
|
25
25
|
* A stable label standing in for a tenant's Paperclip hostname when its owner is anonymous ("anon-3f9a1c"). It keeps one tenant's
|
|
@@ -16,7 +16,7 @@ const ALLOWED = /^[A-Za-z0-9._#]+$/;
|
|
|
16
16
|
const STARTS_OK = /^[A-Za-z0-9]/;
|
|
17
17
|
const HAS_NUMBER_OR_SYMBOL = /[0-9._#]/;
|
|
18
18
|
/** Compared against the letters-only form of a name, so "admin.1" and "root_9" are caught too. */
|
|
19
|
-
const RESERVED = new Set(["admin", "administrator", "root", "system", "support", "staff", "official", "moderator", "papercliped", "paperclip", "cliped", "kpnworld", "
|
|
19
|
+
const RESERVED = new Set(["admin", "administrator", "root", "system", "support", "staff", "official", "moderator", "papercliped", "paperclip", "cliped", "kpnworld", "anthropic", "claude", "openai", "chatgpt"]);
|
|
20
20
|
export const usernameKey = (username) => username.toLowerCase();
|
|
21
21
|
export function validateUsername(raw) {
|
|
22
22
|
if (typeof raw !== "string")
|
package/dist/cli.js
CHANGED
|
@@ -3,17 +3,17 @@ import { readConfig, readDatabase, readHttpConfig } from "./config.js";
|
|
|
3
3
|
import { consolidatedSql, migrate } from "./migrate.js";
|
|
4
4
|
import { OAuthProvider } from "./oauth/provider.js";
|
|
5
5
|
import { createStore } from "./store-factory.js";
|
|
6
|
-
const USAGE = `paperclip-bridge-admin <command>
|
|
7
|
-
|
|
8
|
-
schema-sql print ONE paste-able SQL script (all migrations) for the Supabase SQL editor
|
|
9
|
-
migrate apply database migrations (uses DATABASE_MIGRATE_URL if set, else DATABASE_URL; needs a direct/session connection)
|
|
10
|
-
users list accounts: real username, alias (if anonymous), when last signed in (OPERATOR ONLY: this un-masks aliases)
|
|
11
|
-
delete-user <username> delete an account, its stored credential and grants (asks that Paperclip to revoke the key)
|
|
12
|
-
grants list live grants (no secrets are shown)
|
|
13
|
-
revoke <id> revoke one grant and delete its stored credential
|
|
14
|
-
sweep revoke grants idle longer than BRIDGE_IDLE_REVOKE_DAYS
|
|
15
|
-
revoke-all --yes incident response: revoke EVERY grant, delete all stored credentials, ask each tenant's Paperclip to revoke its key
|
|
16
|
-
rotate-keys re-seal stored credentials with the current BRIDGE_SECRET (put the old one in BRIDGE_SECRET_PREVIOUS first)
|
|
6
|
+
const USAGE = `paperclip-bridge-admin <command>
|
|
7
|
+
|
|
8
|
+
schema-sql print ONE paste-able SQL script (all migrations) for the Supabase SQL editor
|
|
9
|
+
migrate apply database migrations (uses DATABASE_MIGRATE_URL if set, else DATABASE_URL; needs a direct/session connection)
|
|
10
|
+
users list accounts: real username, alias (if anonymous), when last signed in (OPERATOR ONLY: this un-masks aliases)
|
|
11
|
+
delete-user <username> delete an account, its stored credential and grants (asks that Paperclip to revoke the key)
|
|
12
|
+
grants list live grants (no secrets are shown)
|
|
13
|
+
revoke <id> revoke one grant and delete its stored credential
|
|
14
|
+
sweep revoke grants idle longer than BRIDGE_IDLE_REVOKE_DAYS
|
|
15
|
+
revoke-all --yes incident response: revoke EVERY grant, delete all stored credentials, ask each tenant's Paperclip to revoke its key
|
|
16
|
+
rotate-keys re-seal stored credentials with the current BRIDGE_SECRET (put the old one in BRIDGE_SECRET_PREVIOUS first)
|
|
17
17
|
`;
|
|
18
18
|
async function main() {
|
|
19
19
|
const [cmd, arg] = process.argv.slice(2);
|
package/dist/http.js
CHANGED
|
@@ -4,7 +4,12 @@ import { migrate } from "./migrate.js";
|
|
|
4
4
|
import { OAuthProvider } from "./oauth/provider.js";
|
|
5
5
|
import { createHttpServer } from "./server.js";
|
|
6
6
|
import { ManageRoutes } from "./manage/routes.js";
|
|
7
|
+
import { PublicApiRoutes } from "./public-api/routes.js";
|
|
7
8
|
import { SiteRoutes } from "./site/site.js";
|
|
9
|
+
import { VERSION } from "./version.js";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { parseHosts } from "./web/hosts.js";
|
|
12
|
+
import { WebAppRoutes } from "./web/routes.js";
|
|
8
13
|
import { createStore } from "./store-factory.js";
|
|
9
14
|
import { AuditRecorder, NodeReporter, SystemSampler } from "./telemetry/recorder.js";
|
|
10
15
|
import { MemoryStore } from "./oauth/store.js";
|
|
@@ -24,7 +29,8 @@ async function main() {
|
|
|
24
29
|
console.error("Refusing to start: set BRIDGE_TOKEN (e.g. `openssl rand -hex 32`) and/or BRIDGE_OAUTH=1. The bridge exposes control of your agents.");
|
|
25
30
|
process.exit(1);
|
|
26
31
|
}
|
|
27
|
-
// One store serves OAuth state, accounts and audit telemetry
|
|
32
|
+
// One store serves OAuth state, accounts and audit telemetry. The public stats API reads aggregates from it; the private
|
|
33
|
+
// operator dashboard (a separate, access-controlled service) reads the same telemetry tables.
|
|
28
34
|
const store = http.oauth ? createStore(http.oauth) : new MemoryStore();
|
|
29
35
|
try {
|
|
30
36
|
await store.ping();
|
|
@@ -56,13 +62,21 @@ async function main() {
|
|
|
56
62
|
const site = siteUrl
|
|
57
63
|
? new SiteRoutes({
|
|
58
64
|
url: siteUrl,
|
|
65
|
+
mcpUrl: http.oauth?.issuer ?? http.publicUrl ?? undefined,
|
|
59
66
|
contact: process.env.SITE_CONTACT?.trim() || "https://github.com/OpenSourcx/papercliped/issues",
|
|
60
67
|
effective: process.env.SITE_EFFECTIVE_DATE?.trim() || "2026-10-06",
|
|
61
68
|
stats: async () => ({ users: await store.countAccounts(), connections: await store.liveGrantCount() }),
|
|
62
69
|
})
|
|
63
70
|
: undefined;
|
|
64
|
-
const
|
|
65
|
-
|
|
71
|
+
const publicApi = new PublicApiRoutes({ store, version: VERSION, docsUrl: "https://papercliped.co/docs/public-api", publicUrl: siteUrl ?? undefined, proxyHops: http.oauth?.proxyHops ?? 0 });
|
|
72
|
+
// The React website, when built (web/dist next to the package, or WEB_DIST); otherwise the bridge's own Markdown pages.
|
|
73
|
+
const hosts = parseHosts(process.env.PUBLIC_HOSTS);
|
|
74
|
+
const webDir = WebAppRoutes.locate(process.env, fileURLToPath(new URL("../web/dist", import.meta.url)));
|
|
75
|
+
const web = webDir ? new WebAppRoutes({ dir: webDir, hosts, forumUrl: process.env.FORUM_URL?.trim() || null }) : undefined;
|
|
76
|
+
if (web)
|
|
77
|
+
console.error(`website: ${webDir}${process.env.PUBLIC_HOSTS ? ` (hosts: ${process.env.PUBLIC_HOSTS})` : ""}`);
|
|
78
|
+
const manage = provider && http.oauth?.mode === "multi" ? new ManageRoutes(provider) : undefined;
|
|
79
|
+
const server = createHttpServer(config, http, { oauth: provider, recorder, site, manage, publicApi, web });
|
|
66
80
|
server.listen(http.port, http.host, () => {
|
|
67
81
|
const o = http.oauth;
|
|
68
82
|
console.error(`paperclip-bridge listening on http://${http.host}:${http.port}` +
|