mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-28 12:04:41 +01:00
fix(skills): rewrite live agent text and the upload script onto the token-free API helper
Every live skill and reference the agent reads at run time still taught curl with the run bearer as a command-line argument. Rewrite each teaching occurrence onto the paperclip-api helper, which reads the token from the environment and adds it itself. State the reason next to each rewrite: a command-line argument appears in a process listing and in a shell history file. Regenerate the skills-catalog manifest for the three bundled skill files that changed. Rewrite the artifact upload script onto the same helper. The helper rejects a carriage return, a line feed, a double quote, or a backslash in the uploaded file name instead of escaping it, so a path carrying one of those characters now fails with a clear message. The script fails with a clear message when the helper is not on PATH, and does not fall back to curl. Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
c164289df0
commit
3ae75a1d0e
13 files changed
+134
-255
No files matched your search
@@ -110,25 +110,22 @@ workspace that should be freshly ready.
|
||||
|
||||
## Managed start / stop
|
||||
|
||||
Use the runtime-service endpoints on the main control plane. Include
|
||||
`X-Paperclip-Run-Id` so the mutation is associated with the current heartbeat.
|
||||
Use the runtime-service endpoints on the main control plane. Call them with
|
||||
the `paperclip-api` helper program on `PATH`, not `curl`. The helper reads
|
||||
the token from the environment and adds it itself, and it sets
|
||||
`X-Paperclip-Run-Id` for you, so the mutation is still associated with the
|
||||
current heartbeat. The token never becomes a command-line argument; a
|
||||
command-line argument appears in a process listing and in a shell history
|
||||
file.
|
||||
|
||||
```sh
|
||||
curl -sS -X POST \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/stop" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary '{"workspaceCommandId":"service:paperclip-dev"}'
|
||||
paperclip-api POST "/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/stop" \
|
||||
-d '{"workspaceCommandId":"service:paperclip-dev"}'
|
||||
```
|
||||
|
||||
```sh
|
||||
curl -sS -X POST \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/start" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary '{"workspaceCommandId":"service:paperclip-dev"}'
|
||||
paperclip-api POST "/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/start" \
|
||||
-d '{"workspaceCommandId":"service:paperclip-dev"}'
|
||||
```
|
||||
|
||||
If the API returns an existing service, treat that as a candidate only. Verify
|
||||
@@ -179,12 +176,8 @@ If the port owner is a sibling workspace's process (port squat):
|
||||
service and re-run the identity check:
|
||||
|
||||
```sh
|
||||
curl -sS -X POST \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/restart" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary '{"workspaceCommandId":"service:paperclip-dev"}'
|
||||
paperclip-api POST "/api/execution-workspaces/$EXECUTION_WORKSPACE_ID/runtime-services/restart" \
|
||||
-d '{"workspaceCommandId":"service:paperclip-dev"}'
|
||||
```
|
||||
|
||||
6. A restart only holds if the conflicting owner stays stopped. If the port is
|
||||
@@ -253,12 +246,13 @@ process after managed stop fails. Do not kill unrelated processes.
|
||||
## Verify main control-plane runtime state
|
||||
|
||||
Read the execution workspace from the main API and inspect the runtime service
|
||||
record.
|
||||
record. Use the `paperclip-api` helper program on `PATH`, not `curl`; the
|
||||
helper reads the token from the environment and adds it itself, so the token
|
||||
never becomes a command-line argument. A command-line argument appears in a
|
||||
process listing and in a shell history file.
|
||||
|
||||
```sh
|
||||
curl -sS \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
|
||||
paperclip-api GET "/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" | jq
|
||||
```
|
||||
|
||||
The target service should show:
|
||||
@@ -275,12 +269,13 @@ stale process through the managed runtime.
|
||||
## Verify served workspace runtime state
|
||||
|
||||
The cloned Paperclip app must also know about the service. Query the same
|
||||
execution workspace through the served app when agent auth is available there:
|
||||
execution workspace through the served app when agent auth is available
|
||||
there. Point the helper at the served app for this one call by setting
|
||||
`PAPERCLIP_API_URL` only for that command, so the token still never becomes a
|
||||
command-line argument:
|
||||
|
||||
```sh
|
||||
curl -sS \
|
||||
"$SERVICE_URL/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
|
||||
PAPERCLIP_API_URL="$SERVICE_URL" paperclip-api GET "/api/execution-workspaces/$EXECUTION_WORKSPACE_ID" | jq
|
||||
```
|
||||
|
||||
The served app should agree that the service is `running` / `healthy` at the
|
||||
@@ -298,10 +293,8 @@ Minimum API checks:
|
||||
|
||||
```sh
|
||||
curl -sS "$SERVICE_URL/api/health" | jq '.status, .bootstrapStatus'
|
||||
curl -sS "$SERVICE_URL/api/companies" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
|
||||
curl -sS "$SERVICE_URL/api/agents/me" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" | jq
|
||||
PAPERCLIP_API_URL="$SERVICE_URL" paperclip-api GET "/api/companies" | jq
|
||||
PAPERCLIP_API_URL="$SERVICE_URL" paperclip-api GET "/api/agents/me" | jq
|
||||
```
|
||||
|
||||
Then verify at least one expected cloned product record through the API, such
|
||||
|
||||
+6
-6
@@ -58,11 +58,12 @@ Every proposal must satisfy all of these:
|
||||
|
||||
## Procedure
|
||||
|
||||
Call every route below with the `paperclip-api` helper program on `PATH`, not `curl`. The helper reads the access token from the environment and adds the `Authorization` header itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
### 1) Confirm target and scope
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/agents/<targetAgentId>" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/agents/<targetAgentId>"
|
||||
```
|
||||
|
||||
Record `name`, `role`, `reportsTo`, `adapterType`, `adapterConfig.instructionsFilePath` (where `AGENTS.md` lives), and current assigned skills via `GET /api/agents/<targetAgentId>/skills`. Refuse and exit if `targetAgentId == $PAPERCLIP_AGENT_ID`.
|
||||
@@ -70,15 +71,14 @@ Record `name`, `role`, `reportsTo`, `adapterType`, `adapterConfig.instructionsFi
|
||||
### 2) Pull the recent record
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues?assigneeAgentId=<targetAgentId>&status=done,in_review,blocked&limit=25" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/companies/$PAPERCLIP_COMPANY_ID/issues?assigneeAgentId=<targetAgentId>&status=done,in_review,blocked&limit=25"
|
||||
```
|
||||
|
||||
For each issue, pull the trajectory substrate — the issue body and its comments:
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/issues/<issueId>" -H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
curl -sS "$PAPERCLIP_API_URL/api/issues/<issueId>/comments" -H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/issues/<issueId>"
|
||||
paperclip-api GET "/api/issues/<issueId>/comments"
|
||||
```
|
||||
|
||||
Keep status transitions, blocker reasons, reviewer comments, approval outcomes, human corrections, and PR-link comments. Comments are the closest thing Paperclip has to an execution trace — treat them as first-class evidence.
|
||||
|
||||
+6
-19
@@ -24,33 +24,20 @@ Use this skill in one of two modes:
|
||||
|
||||
Agent-authored cards require `tasks:assign`, remain company-scoped, and are available only when `enableStatusCards` is enabled. An agent may manage only cards it authored, may author at most 20 cards, and may send at most 4,000 characters in `interestPrompt`.
|
||||
|
||||
Normalize the run-provided API base and create a manual card:
|
||||
Create a manual card with the `paperclip-api` helper program on `PATH`, not `curl`. The helper reads the run-provided API base and the access token from the environment and adds them itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
```bash
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_URL%/}"
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_BASE%/api}"
|
||||
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"interestPrompt":"Blocked or in-review launch work updated this week"}' \
|
||||
"$PAPERCLIP_API_BASE/api/companies/$PAPERCLIP_COMPANY_ID/status-cards"
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/status-cards" \
|
||||
-d '{"interestPrompt":"Blocked or in-review launch work updated this week"}'
|
||||
```
|
||||
|
||||
Creation returns `201` and queues compilation automatically. Save the returned card id. To refine an owned card or request a refresh:
|
||||
|
||||
```bash
|
||||
curl -sS -X PATCH \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"interestPrompt":"Blocked or in-review launch work updated this week. Call out the single next decision."}' \
|
||||
"$PAPERCLIP_API_BASE/api/status-cards/$STATUS_CARD_ID"
|
||||
paperclip-api PATCH "/api/status-cards/$STATUS_CARD_ID" \
|
||||
-d '{"interestPrompt":"Blocked or in-review launch work updated this week. Call out the single next decision."}'
|
||||
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"full":false}' \
|
||||
"$PAPERCLIP_API_BASE/api/status-cards/$STATUS_CARD_ID/refresh"
|
||||
paperclip-api POST "/api/status-cards/$STATUS_CARD_ID/refresh" -d '{"full":false}'
|
||||
```
|
||||
|
||||
Do not call `/query` or `/summary` while authoring. Those write-back routes are reserved for the assigned Summarizer generation issue and run.
|
||||
|
||||
+2
-6
@@ -58,7 +58,7 @@ Use these routes directly. Do not guess unscoped `/api/issues` or alternate summ
|
||||
- Gather execution-workspace issues: `GET /api/companies/{companyId}/issues?executionWorkspaceId=...`
|
||||
- Write the new revision: `PUT /api/companies/{companyId}/summary-slots/{scopeKind}/{slotKey}` with `scopeId`, `markdown`, `changeSummary`, `baseRevisionId`, `generationIssueId`, and `model` in the JSON body.
|
||||
|
||||
For `workspaces_overview`, omit `scopeId` from the read query and send it as `null` in the write body. All calls use the run-scoped Paperclip API URL and bearer token already present in the environment.
|
||||
For `workspaces_overview`, omit `scopeId` from the read query and send it as `null` in the write body. Call every route with the `paperclip-api` helper program on `PATH`, not `curl`. The helper reads the run-scoped Paperclip API URL and the access token from the environment and adds them itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
Complete project-slot write example:
|
||||
|
||||
@@ -89,11 +89,7 @@ jq -n \
|
||||
generationIssueId: $generationIssueId,
|
||||
model: $model
|
||||
}' |
|
||||
curl -sS -X PUT \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$PAPERCLIP_API_URL/api/companies/$COMPANY_ID/summary-slots/project/header" \
|
||||
--data-binary @-
|
||||
paperclip-api PUT "/api/companies/$COMPANY_ID/summary-slots/project/header" -d @-
|
||||
```
|
||||
|
||||
## Procedure
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"schemaVersion": 1,
|
||||
"packageName": "@paperclipai/skills-catalog",
|
||||
"packageVersion": "0.3.1",
|
||||
"generatedAt": "2026-08-11T15:05:34.526Z",
|
||||
"generatedAt": "2026-09-16T10:58:12.842Z",
|
||||
"skills": [
|
||||
{
|
||||
"id": "paperclipai:bundled:docs:doc-maintenance",
|
||||
@@ -102,11 +102,11 @@
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"kind": "skill",
|
||||
"sizeBytes": 12028,
|
||||
"sha256": "083d5fa8dc43c3ce7747e89c1afb6f6f42d0603a31808bffc5f8e56735f2d865"
|
||||
"sizeBytes": 12120,
|
||||
"sha256": "d0e6e25da37111b57cca8ec758bde7c4c8f24e6b64770ddacb795bbb75c5883b"
|
||||
}
|
||||
],
|
||||
"contentHash": "sha256:f2592d81f616628a3eba92636616424f2eed82ec22bb504240a58f6be115e407"
|
||||
"contentHash": "sha256:a4208745a552233befb60829d9c45ad8d30f3a78a7c9171c76957ecf884cbbd6"
|
||||
},
|
||||
{
|
||||
"id": "paperclipai:bundled:paperclip-operations:status-card-query",
|
||||
@@ -137,11 +137,11 @@
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"kind": "skill",
|
||||
"sizeBytes": 6368,
|
||||
"sha256": "0c6140f51d503cbadc98723ce54c0def48e7e31ea159b0935b8ad6369291498a"
|
||||
"sizeBytes": 6201,
|
||||
"sha256": "1d024ce62b394a41174afd06b4cf0ae120d58a0dc6171158c722736acf750796"
|
||||
}
|
||||
],
|
||||
"contentHash": "sha256:2b6e53bf8491f027afdbd6961ad84f677cfd385a0a6a73616ea84111ca8886ed"
|
||||
"contentHash": "sha256:8d605dd7a19bc4eafa9b6e344440573c38b02652d7679f06e1504d0f77811f3f"
|
||||
},
|
||||
{
|
||||
"id": "paperclipai:bundled:paperclip-operations:summarize-status",
|
||||
@@ -172,11 +172,11 @@
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"kind": "skill",
|
||||
"sizeBytes": 8877,
|
||||
"sha256": "4cb3d177f5d16e3cc2052d2f80e7fb245ab95e79c1e947abdc84cd4ee3d8c61e"
|
||||
"sizeBytes": 8980,
|
||||
"sha256": "a0edcafcf1cf7ec04cc54aa956b0206d9e5afd81538eaeb390369b814b369e06"
|
||||
}
|
||||
],
|
||||
"contentHash": "sha256:3e49ee2b8d83d1371fb0f59197bb96c46b64f722540405f46d4a5a756fbe1b89"
|
||||
"contentHash": "sha256:86a89739d83ca40d330697c08e1fa3fed9273cced9e4015c36dc9c3d454d3b66"
|
||||
},
|
||||
{
|
||||
"id": "paperclipai:bundled:paperclip-operations:task-planning",
|
||||
|
||||
@@ -16,7 +16,7 @@ You are a board-level assistant helping a human manage their AI-agent company th
|
||||
- `PAPERCLIP_API_URL` — base URL of the Paperclip server (e.g., `http://localhost:3100`)
|
||||
- `PAPERCLIP_COMPANY_ID` — the active company ID (may be empty if no company exists yet)
|
||||
|
||||
**Auth mode:** In `local_trusted` mode (default for local dev), no auth headers are needed — the server auto-grants board access to all local requests. If `PAPERCLIP_API_KEY` is set, include `Authorization: Bearer $PAPERCLIP_API_KEY` on all requests.
|
||||
**Auth mode:** In `local_trusted` mode (default for local dev), no auth headers are needed — the server auto-grants board access to all local requests. If `PAPERCLIP_API_KEY` is set, call the API with the `paperclip-api` helper program on `PATH` instead of `curl`. The helper reads the token from the environment and adds the `Authorization` header itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
**Making API calls:** Use `curl -sS` via bash. All endpoints are under `/api`. All request/response bodies are JSON. Always use `Content-Type: application/json` on POST/PATCH/PUT requests.
|
||||
|
||||
|
||||
@@ -21,29 +21,30 @@ If you do not have this permission, escalate to your CEO or board.
|
||||
|
||||
## Workflow
|
||||
|
||||
Call every route below with the `paperclip-api` helper program on `PATH`, not
|
||||
`curl`. The helper reads the token from the environment and adds it itself,
|
||||
so the token never becomes a command-line argument. A command-line argument
|
||||
appears in a process listing and in a shell history file.
|
||||
|
||||
### 1. Confirm identity and company context
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/agents/me" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/agents/me"
|
||||
```
|
||||
|
||||
### 2. Discover adapter configuration for this Paperclip instance
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/llms/agent-configuration.txt" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/llms/agent-configuration.txt"
|
||||
|
||||
# Then the specific adapter you plan to use, e.g. claude_local:
|
||||
curl -sS "$PAPERCLIP_API_URL/llms/agent-configuration/claude_local.txt" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/llms/agent-configuration/claude_local.txt"
|
||||
```
|
||||
|
||||
### 3. Compare existing agent configurations
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-configurations" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/companies/$PAPERCLIP_COMPANY_ID/agent-configurations"
|
||||
```
|
||||
|
||||
Note naming, icon, reporting-line, and adapter conventions the company already follows.
|
||||
@@ -67,8 +68,7 @@ State which path you took in your hire-request comment so the board can see the
|
||||
### 5. Discover allowed agent icons
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/llms/agent-icons.txt" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/llms/agent-icons.txt"
|
||||
```
|
||||
|
||||
### 6. Draft the new hire config
|
||||
@@ -96,10 +96,7 @@ Before submitting, walk the draft-review checklist end-to-end and fix any item t
|
||||
### 8. Submit hire request
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-hires" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/agent-hires" -d '{
|
||||
"name": "CTO",
|
||||
"role": "cto",
|
||||
"title": "Chief Technology Officer",
|
||||
@@ -122,32 +119,24 @@ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-h
|
||||
- when the board approves, you will be woken with `PAPERCLIP_APPROVAL_ID`; read linked issues and close/comment follow-up
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/approvals/<approval-id>" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/approvals/<approval-id>"
|
||||
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/approvals/<approval-id>/comments" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
paperclip-api POST "/api/approvals/<approval-id>/comments" \
|
||||
-d '{"body":"## CTO hire request submitted\n\n- Approval: [<approval-id>](/approvals/<approval-id>)\n- Pending agent: [<agent-ref>](/agents/<agent-url-key-or-id>)\n- Source issue: [<issue-ref>](/issues/<issue-identifier-or-id>)\n\nUpdated prompt and adapter config per board feedback."}'
|
||||
```
|
||||
|
||||
If the approval already exists and needs manual linking to the issue:
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/issues/<issue-id>/approvals" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"approvalId":"<approval-id>"}'
|
||||
paperclip-api POST "/api/issues/<issue-id>/approvals" -d '{"approvalId":"<approval-id>"}'
|
||||
```
|
||||
|
||||
After approval is granted, run this follow-up loop:
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/approvals/$PAPERCLIP_APPROVAL_ID" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/approvals/$PAPERCLIP_APPROVAL_ID"
|
||||
|
||||
curl -sS "$PAPERCLIP_API_URL/api/approvals/$PAPERCLIP_APPROVAL_ID/issues" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/approvals/$PAPERCLIP_APPROVAL_ID/issues"
|
||||
```
|
||||
|
||||
For each linked issue, either:
|
||||
|
||||
@@ -17,7 +17,7 @@ In Paperclip, **task** and **issue** refer to the same work item. The UI may use
|
||||
|
||||
## Authentication
|
||||
|
||||
Env vars auto-injected: `PAPERCLIP_AGENT_ID`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_API_URL`, `PAPERCLIP_RUN_ID`. Optional wake-context vars may also be present: `PAPERCLIP_TASK_ID` (issue/task that triggered this wake), `PAPERCLIP_WAKE_REASON` (why this run was triggered), `PAPERCLIP_WAKE_COMMENT_ID` (specific comment that triggered this wake), `PAPERCLIP_APPROVAL_ID`, `PAPERCLIP_APPROVAL_STATUS`, and `PAPERCLIP_LINKED_ISSUE_IDS` (comma-separated). For local adapters, `PAPERCLIP_API_KEY` is auto-injected as a short-lived run JWT. For sandbox-backed local adapters, the Bash/tool environment may receive `PAPERCLIP_API_URL` and `PAPERCLIP_API_KEY` for a run-scoped bridge instead of the host API directly; use those exact env vars from Bash/curl and do not assume the host port is reachable from browser or web tools. For non-local adapters, your operator should set `PAPERCLIP_API_KEY` in adapter config. All requests use `Authorization: Bearer $PAPERCLIP_API_KEY`. All endpoints are under `/api`. Use JSON except for multipart attachment uploads and binary content downloads. Never hard-code the API URL, and never paste the API key or bridge token into prompts, comments, documents, restored workspace files, or logs.
|
||||
Env vars auto-injected: `PAPERCLIP_AGENT_ID`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_API_URL`, `PAPERCLIP_RUN_ID`. Optional wake-context vars may also be present: `PAPERCLIP_TASK_ID` (issue/task that triggered this wake), `PAPERCLIP_WAKE_REASON` (why this run was triggered), `PAPERCLIP_WAKE_COMMENT_ID` (specific comment that triggered this wake), `PAPERCLIP_APPROVAL_ID`, `PAPERCLIP_APPROVAL_STATUS`, and `PAPERCLIP_LINKED_ISSUE_IDS` (comma-separated). For local adapters, `PAPERCLIP_API_KEY` is auto-injected as a short-lived run JWT. For sandbox-backed local adapters, the Bash/tool environment may receive `PAPERCLIP_API_URL` and `PAPERCLIP_API_KEY` for a run-scoped bridge instead of the host API directly; use those exact env vars from Bash and do not assume the host port is reachable from browser or web tools. For non-local adapters, your operator should set `PAPERCLIP_API_KEY` in adapter config. Call every route with the `paperclip-api` helper program on `PATH`, not `curl`. The helper reads the token from the environment and adds the `Authorization` header itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file. All endpoints are under `/api`. Use JSON except for multipart attachment uploads and binary content downloads. Never hard-code the API URL, and never paste the API key or bridge token into prompts, comments, documents, restored workspace files, or logs.
|
||||
|
||||
Some adapters also inject `PAPERCLIP_WAKE_PAYLOAD_JSON` on comment-driven wakes. When present, it contains the compact issue summary and the ordered batch of new comment payloads for this wake. Use it first. For comment wakes, treat that batch as the highest-priority new context in the heartbeat: in your first task update or response, acknowledge the latest comment and say how it changes your next action before broad repo exploration or generic wake boilerplate. Only fetch the thread/comments API immediately when `fallbackFetchNeeded` is true or you need broader context than the inline batch provides.
|
||||
|
||||
@@ -115,12 +115,11 @@ Overrides and special cases:
|
||||
- **Blocked-task dedup:** before touching a `blocked` task, check the thread. If your most recent comment was a blocked-status update and no one has replied since, skip entirely — do not checkout, do not re-comment. Only re-engage on new context (comment, status change, event wake).
|
||||
- Nothing assigned and no valid mention handoff → exit the heartbeat.
|
||||
|
||||
**Step 5 — Checkout.** You MUST checkout before doing any work. Include the run ID header:
|
||||
**Step 5 — Checkout.** You MUST checkout before doing any work. `paperclip-api` sets the run ID header for you:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/checkout
|
||||
Headers: Authorization: Bearer $PAPERCLIP_API_KEY, X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID
|
||||
{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
paperclip-api POST "/api/issues/{issueId}/checkout" \
|
||||
-d '{"agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"]}'
|
||||
```
|
||||
|
||||
If already checked out by you, returns normally. If owned by another agent: `409 Conflict` — stop, pick a different task. **Never retry a 409.**
|
||||
@@ -520,20 +519,16 @@ Before proposing a credential you MUST read the "Agent secret proposals" section
|
||||
|
||||
## Reading Granted Secrets
|
||||
|
||||
When authenticated with the current run's agent JWT, list the secrets available to that run before fetching a value:
|
||||
When authenticated with the current run's agent JWT, list the secrets available to that run before fetching a value. Use the `paperclip-api` helper program on `PATH`, not `curl`; the helper reads the token from the environment and adds it itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
```bash
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_URL%/}"
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_BASE%/api}"
|
||||
curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
"$PAPERCLIP_API_BASE/api/agents/me/secrets"
|
||||
paperclip-api GET "/api/agents/me/secrets"
|
||||
```
|
||||
|
||||
The list is metadata-only. Fetch a specific value only when needed; the request has no body:
|
||||
|
||||
```bash
|
||||
curl -s -X POST -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
"$PAPERCLIP_API_BASE/api/agents/me/secrets/github_token/value"
|
||||
paperclip-api POST "/api/agents/me/secrets/github_token/value"
|
||||
```
|
||||
|
||||
- An `env.*` secret binding also grants API read access; `access.*` bindings grant API access without env injection.
|
||||
|
||||
@@ -1474,21 +1474,15 @@ Terminal states: `done`, `cancelled`
|
||||
|
||||
**Never paste a credential into a comment, document, file, or transcript.** When a credential is supplied to an agent or returned by a secure flow — pasted by a user, returned by an OAuth flow, delivered by email, or obtained from another secure source — send it directly to `POST /api/agents/me/secret-proposals` using the current run-bound agent JWT. Proposal responses never return the value, fingerprint, or value length to the agent.
|
||||
|
||||
Keep the credential in memory or pass it directly from the secure source; do not place the literal value in the command text or echo it. The example assumes `PROPOSED_SECRET_VALUE` is already populated without printing it:
|
||||
Keep the credential in memory or pass it directly from the secure source; do not place the literal value in the command text or echo it. Use the `paperclip-api` helper program on `PATH`, not `curl`; the helper reads the token from the environment and adds it itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file. The example assumes `PROPOSED_SECRET_VALUE` is already populated without printing it:
|
||||
|
||||
```bash
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_URL%/}"
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_BASE%/api}"
|
||||
jq -n \
|
||||
--arg name "integrations/vendor/api-token" \
|
||||
--arg value "$PROPOSED_SECRET_VALUE" \
|
||||
--arg justification "Credential supplied for the current task" \
|
||||
'{kind:"secret", name:$name, value:$value, justification:$justification}' |
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @- \
|
||||
"$PAPERCLIP_API_BASE/api/agents/me/secret-proposals"
|
||||
paperclip-api POST "/api/agents/me/secret-proposals" -d @-
|
||||
unset PROPOSED_SECRET_VALUE
|
||||
```
|
||||
|
||||
@@ -1514,11 +1508,7 @@ jq -n \
|
||||
--arg configPath "env.VENDOR_API_TOKEN" \
|
||||
--arg justification "Inject the approved credential into my adapter environment" \
|
||||
'{kind:"binding", secretProposalId:$secretProposalId, configPath:$configPath, justification:$justification}' |
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @- \
|
||||
"$PAPERCLIP_API_BASE/api/agents/me/secret-proposals"
|
||||
paperclip-api POST "/api/agents/me/secret-proposals" -d @-
|
||||
```
|
||||
|
||||
A binding must specify exactly one of `secretProposalId`, `secretId`, or `sourceConfigPath`. `configPath` accepts `env.<KEY>` for environment injection or `access.<ALIAS>` for API-only access. Under the default `self_and_reports` policy, `targetAgentId` may identify a downward report of the proposer; omitting it targets the proposer. Other targets are denied, and approval rechecks the current chain of command.
|
||||
@@ -1528,18 +1518,12 @@ A binding must specify exactly one of `secretProposalId`, `secretId`, or `source
|
||||
Use `sourceConfigPath` when the secret is already bound to the proposing agent. The server resolves that agent's own `env.*` or `access.*` binding, so the request never needs a secret ID or `secretRef`:
|
||||
|
||||
```bash
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_URL%/}"
|
||||
PAPERCLIP_API_BASE="${PAPERCLIP_API_BASE%/api}"
|
||||
jq -n \
|
||||
--arg sourceConfigPath "access.openai_api_key" \
|
||||
--arg configPath "access.evals_openai_api_key" \
|
||||
--arg justification "Use the existing OpenAI credential under the eval-specific alias" \
|
||||
'{kind:"binding", sourceConfigPath:$sourceConfigPath, configPath:$configPath, justification:$justification}' |
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @- \
|
||||
"$PAPERCLIP_API_BASE/api/agents/me/secret-proposals"
|
||||
paperclip-api POST "/api/agents/me/secret-proposals" -d @-
|
||||
```
|
||||
|
||||
`sourceConfigPath` must name an existing binding on the proposing agent; another agent's path and an unknown path both return `404`. Omit `targetAgentId` to bind the alias back to yourself. Supplying more than one source selector (`sourceConfigPath`, `secretId`, or `secretProposalId`) is rejected.
|
||||
@@ -1557,9 +1541,7 @@ The card uses `continuationPolicy: "wake_assignee"`. On resolution the issue ass
|
||||
**After any secret card resolves, re-verify through `GET /api/agents/me/secrets`. Acceptance is not execution.** On the resumed run, call:
|
||||
|
||||
```bash
|
||||
curl -s \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
"$PAPERCLIP_API_BASE/api/agents/me/secrets"
|
||||
paperclip-api GET "/api/agents/me/secrets"
|
||||
```
|
||||
|
||||
Confirm the expected secret metadata and delivery are present before using the new binding. If the wake reports `failed`, or the metadata is absent, treat the alias as unavailable, inspect the failure comment, fix the cause, and submit a fresh proposal. Never infer success merely because the card says accepted.
|
||||
|
||||
@@ -49,36 +49,23 @@ Annotate the work product with `metadata.resourceRef`:
|
||||
optional positive integers. `relativePath` must be relative to the selected
|
||||
workspace root; do not use host-local absolute paths in `resourceRef`.
|
||||
|
||||
Create the work product with:
|
||||
Create the work product with the `paperclip-api` helper program on `PATH`, not `curl`. The helper reads the token from the environment and adds it itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
```bash
|
||||
curl -sS -X POST \
|
||||
"$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @workspace-file-work-product.json
|
||||
paperclip-api POST "/api/issues/$PAPERCLIP_TASK_ID/work-products" -d @workspace-file-work-product.json
|
||||
```
|
||||
|
||||
If the helper is unavailable, use the Paperclip API directly:
|
||||
If the upload helper is unavailable, use the Paperclip API directly:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST \
|
||||
"$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-F 'file=@"path/to/output.webm";type=video/webm'
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \
|
||||
-F 'file=@path/to/output.webm;type=video/webm'
|
||||
```
|
||||
|
||||
Then create a work product when the file is the deliverable. The server canonicalizes attachment-backed artifact metadata from the `attachmentId`:
|
||||
|
||||
```bash
|
||||
curl -sS -X POST \
|
||||
"$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary '{
|
||||
paperclip-api POST "/api/issues/$PAPERCLIP_TASK_ID/work-products" -d '{
|
||||
"type": "artifact",
|
||||
"provider": "paperclip",
|
||||
"title": "Walkthrough render",
|
||||
|
||||
@@ -78,17 +78,18 @@ Browse, inspect, and install catalog skills before reaching for an external
|
||||
source. Bundled skills are the curated defaults for any company; optional
|
||||
skills are role- or domain-specific.
|
||||
|
||||
Call every route in this reference with the `paperclip-api` helper program on
|
||||
`PATH`, not `curl`. The helper reads the token from the environment and adds
|
||||
it itself, so the token never becomes a command-line argument. A
|
||||
command-line argument appears in a process listing and in a shell history
|
||||
file.
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/skills/catalog?kind=bundled" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/skills/catalog?kind=bundled"
|
||||
|
||||
curl -sS "$PAPERCLIP_API_URL/api/skills/catalog/ref?ref=github-pr-workflow" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/skills/catalog/ref?ref=github-pr-workflow"
|
||||
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/install-catalog" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/skills/install-catalog" -d '{
|
||||
"catalogSkillId": "paperclipai:bundled:software-development:github-pr-workflow"
|
||||
}'
|
||||
```
|
||||
@@ -116,10 +117,7 @@ Import using a **skills.sh URL**, a key-style source string, a GitHub URL, or a
|
||||
### Example: skills.sh import (preferred)
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" -d '{
|
||||
"source": "https://skills.sh/google-labs-code/stitch-skills/design-md"
|
||||
}'
|
||||
```
|
||||
@@ -127,10 +125,7 @@ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/
|
||||
Or equivalently using the key-style string:
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" -d '{
|
||||
"source": "google-labs-code/stitch-skills/design-md"
|
||||
}'
|
||||
```
|
||||
@@ -138,10 +133,7 @@ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/
|
||||
### Example: GitHub import
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/skills/import" -d '{
|
||||
"source": "https://github.com/vercel-labs/agent-browser"
|
||||
}'
|
||||
```
|
||||
@@ -155,27 +147,21 @@ You can also use source strings such as:
|
||||
If the task is to discover skills from the company project workspaces first:
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/scan-projects" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}'
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/skills/scan-projects" -d '{}'
|
||||
```
|
||||
|
||||
## Inspect What Was Installed
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/companies/$PAPERCLIP_COMPANY_ID/skills"
|
||||
```
|
||||
|
||||
Read the skill entry and its `SKILL.md`:
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/<skill-id>" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/companies/$PAPERCLIP_COMPANY_ID/skills/<skill-id>"
|
||||
|
||||
curl -sS "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/skills/<skill-id>/files?path=SKILL.md" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/companies/$PAPERCLIP_COMPANY_ID/skills/<skill-id>/files?path=SKILL.md"
|
||||
```
|
||||
|
||||
## Assign Skills To An Existing Agent
|
||||
@@ -195,10 +181,7 @@ The request must include a merge mode:
|
||||
- `replace` overwrites the complete desired skill set. Use it only after explicit confirmation.
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/agents/<agent-id>/skills/sync" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/agents/<agent-id>/skills/sync" -d '{
|
||||
"mode": "add",
|
||||
"desiredSkills": [
|
||||
"vercel-labs/agent-browser/agent-browser"
|
||||
@@ -209,8 +192,7 @@ curl -sS -X POST "$PAPERCLIP_API_URL/api/agents/<agent-id>/skills/sync" \
|
||||
If you need the current state first:
|
||||
|
||||
```sh
|
||||
curl -sS "$PAPERCLIP_API_URL/api/agents/<agent-id>/skills" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
paperclip-api GET "/api/agents/<agent-id>/skills"
|
||||
```
|
||||
|
||||
## Include Skills During Hire Or Create
|
||||
@@ -218,10 +200,7 @@ curl -sS "$PAPERCLIP_API_URL/api/agents/<agent-id>/skills" \
|
||||
Use the same company skill keys or references in `desiredSkills` when hiring or creating an agent:
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-hires" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/agent-hires" -d '{
|
||||
"name": "QA Browser Agent",
|
||||
"role": "qa",
|
||||
"adapterType": "codex_local",
|
||||
@@ -237,10 +216,7 @@ curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agent-h
|
||||
For direct create without approval:
|
||||
|
||||
```sh
|
||||
curl -sS -X POST "$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/agents" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
paperclip-api POST "/api/companies/$PAPERCLIP_COMPANY_ID/agents" -d '{
|
||||
"name": "QA Browser Agent",
|
||||
"role": "qa",
|
||||
"adapterType": "codex_local",
|
||||
|
||||
@@ -4,11 +4,10 @@ Use this reference when an issue has an isolated execution workspace and you nee
|
||||
|
||||
## Discover the Workspace
|
||||
|
||||
Start from the issue, not from memory:
|
||||
Start from the issue, not from memory. Use the `paperclip-api` helper program on `PATH`, not `curl`; the helper reads the token from the environment and adds it itself, so the token never becomes a command-line argument. A command-line argument appears in a process listing and in a shell history file.
|
||||
|
||||
```sh
|
||||
curl -sS -H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
"$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/heartbeat-context"
|
||||
paperclip-api GET "/api/issues/$PAPERCLIP_TASK_ID/heartbeat-context"
|
||||
```
|
||||
|
||||
Read `currentExecutionWorkspace`:
|
||||
@@ -26,28 +25,13 @@ Prefer Paperclip-managed runtime service controls over manual `pnpm dev &` or ad
|
||||
|
||||
```sh
|
||||
# Start all configured services; waits for configured readiness checks.
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/<workspace-id>/runtime-services/start" \
|
||||
-d '{}'
|
||||
paperclip-api POST "/api/execution-workspaces/<workspace-id>/runtime-services/start" -d '{}'
|
||||
|
||||
# Restart all configured services.
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/<workspace-id>/runtime-services/restart" \
|
||||
-d '{}'
|
||||
paperclip-api POST "/api/execution-workspaces/<workspace-id>/runtime-services/restart" -d '{}'
|
||||
|
||||
# Stop all running services.
|
||||
curl -sS -X POST \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$PAPERCLIP_API_URL/api/execution-workspaces/<workspace-id>/runtime-services/stop" \
|
||||
-d '{}'
|
||||
paperclip-api POST "/api/execution-workspaces/<workspace-id>/runtime-services/stop" -d '{}'
|
||||
```
|
||||
|
||||
To target a configured service, pass one of:
|
||||
|
||||
@@ -50,6 +50,13 @@ require_command() {
|
||||
fi
|
||||
}
|
||||
|
||||
require_paperclip_api() {
|
||||
if ! command -v paperclip-api >/dev/null 2>&1; then
|
||||
printf 'Missing required command: paperclip-api. This program is staged onto PATH inside a Paperclip agent run; it is not available outside one.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
json_bool() {
|
||||
if [[ "${1:-0}" == "1" ]]; then
|
||||
printf 'true'
|
||||
@@ -115,32 +122,20 @@ sha256_text() {
|
||||
|
||||
request_json() {
|
||||
local method="$1"
|
||||
local url="$2"
|
||||
local path="$2"
|
||||
local body="${3:-}"
|
||||
local response_file
|
||||
local status_code
|
||||
|
||||
response_file="$(mktemp)"
|
||||
if [[ -n "$body" ]]; then
|
||||
status_code="$(
|
||||
curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \
|
||||
"$url" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-binary "$body"
|
||||
)"
|
||||
status_code="$(paperclip-api "$method" "$path" -o "$response_file" --status -d "$body")"
|
||||
else
|
||||
status_code="$(
|
||||
curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \
|
||||
"$url" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID"
|
||||
)"
|
||||
status_code="$(paperclip-api "$method" "$path" -o "$response_file" --status)"
|
||||
fi
|
||||
|
||||
if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then
|
||||
printf 'Request failed (%s): %s\n' "$status_code" "$url" >&2
|
||||
printf 'Request failed (%s): %s\n' "$status_code" "$path" >&2
|
||||
cat "$response_file" >&2
|
||||
printf '\n' >&2
|
||||
rm -f "$response_file"
|
||||
@@ -152,33 +147,27 @@ request_json() {
|
||||
}
|
||||
|
||||
upload_file() {
|
||||
local url="$1"
|
||||
local path="$2"
|
||||
local path="$1"
|
||||
local file_path="$2"
|
||||
local content_type="$3"
|
||||
local escaped_path
|
||||
local response_file
|
||||
local status_code
|
||||
local curl_status=0
|
||||
local helper_status=0
|
||||
local indeterminate=0
|
||||
|
||||
escaped_path="${path//\\/\\\\}"
|
||||
escaped_path="${escaped_path//\"/\\\"}"
|
||||
response_file="$(mktemp)"
|
||||
status_code="$(
|
||||
curl -sS -X POST -w '%{http_code}' -o "$response_file" \
|
||||
"$url" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
||||
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
||||
-F "file=@\"${escaped_path}\";type=${content_type}"
|
||||
)" || curl_status=$?
|
||||
paperclip-api POST "$path" -o "$response_file" --status \
|
||||
-F "file=@${file_path};type=${content_type}"
|
||||
)" || helper_status=$?
|
||||
|
||||
if [[ "$curl_status" -ne 0 ]]; then
|
||||
if [[ "$helper_status" -ne 0 ]]; then
|
||||
rm -f "$response_file"
|
||||
return 75
|
||||
fi
|
||||
|
||||
if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then
|
||||
printf 'Upload failed (%s): %s\n' "$status_code" "$url" >&2
|
||||
printf 'Upload failed (%s): %s\n' "$status_code" "$path" >&2
|
||||
cat "$response_file" >&2
|
||||
printf '\n' >&2
|
||||
# The bridge uses a structured 409 outcome after a possibly committed
|
||||
@@ -369,7 +358,6 @@ if [[ -n "$chat_comment" && "$create_work_product" != "1" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
require_command curl
|
||||
require_command jq
|
||||
|
||||
if [[ -z "$title" ]]; then
|
||||
@@ -398,6 +386,8 @@ if [[ "$dry_run" == "1" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
require_paperclip_api
|
||||
|
||||
if [[ -z "${PAPERCLIP_API_URL:-}" || -z "${PAPERCLIP_API_KEY:-}" || -z "${PAPERCLIP_RUN_ID:-}" ]]; then
|
||||
printf 'Missing PAPERCLIP_API_URL, PAPERCLIP_API_KEY, or PAPERCLIP_RUN_ID.\n' >&2
|
||||
exit 1
|
||||
@@ -429,7 +419,7 @@ if [[ -f "$unknown_upload_marker" && "$retry_unknown_upload" != "1" ]]; then
|
||||
lookup_attempts=20
|
||||
fi
|
||||
for ((lookup_attempt = 1; lookup_attempt <= lookup_attempts; lookup_attempt++)); do
|
||||
existing_attachments="$(request_json GET "$api_base/issues/$issue_id/attachments")"
|
||||
existing_attachments="$(request_json GET "/api/issues/$issue_id/attachments")"
|
||||
attachment="$(
|
||||
jq -nc \
|
||||
--argjson attachments "$existing_attachments" \
|
||||
@@ -469,7 +459,7 @@ if [[ -z "$attachment" ]]; then
|
||||
upload_status=0
|
||||
attachment="$(
|
||||
upload_file \
|
||||
"$api_base/companies/$company_id/issues/$issue_id/attachments" \
|
||||
"/api/companies/$company_id/issues/$issue_id/attachments" \
|
||||
"$file_path" \
|
||||
"$content_type"
|
||||
)" || upload_status=$?
|
||||
@@ -537,7 +527,7 @@ if [[ "$create_work_product" == "1" ]]; then
|
||||
work_product="$(
|
||||
request_json \
|
||||
POST \
|
||||
"$api_base/issues/$issue_id/work-products" \
|
||||
"/api/issues/$issue_id/work-products" \
|
||||
"$work_product_payload"
|
||||
)"
|
||||
fi
|
||||
@@ -553,7 +543,7 @@ if [[ -n "$chat_comment" ]]; then
|
||||
chat_response="$(
|
||||
request_json \
|
||||
POST \
|
||||
"$api_base/issues/$issue_id/comments" \
|
||||
"/api/issues/$issue_id/comments" \
|
||||
"$chat_comment_payload"
|
||||
)"
|
||||
chat_comment_id="$(jq -r '.id // empty' <<<"$chat_response")"
|
||||
|
||||
Reference in new issue
Block a user