@penclipai/adapter-codex-local 2026.531.0 → 2026.602.0-canary.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@penclipai/adapter-codex-local",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.602.0-canary.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://github.com/penclipai/paperclip-cn",
|
|
6
6
|
"bugs": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"skills"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@penclipai/adapter-utils": "2026.
|
|
41
|
+
"@penclipai/adapter-utils": "2026.602.0-canary.0",
|
|
42
42
|
"picocolors": "^1.1.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -94,6 +94,12 @@ If `currentParticipant` does not match you, do not try to advance the stage —
|
|
|
94
94
|
- If blocked, move the issue to `blocked` with the unblock owner and exact action needed.
|
|
95
95
|
- Respect budget, pause/cancel, approval gates, execution policy stages, and company boundaries.
|
|
96
96
|
|
|
97
|
+
### Generated Artifacts and Work Products
|
|
98
|
+
|
|
99
|
+
When work produces a user-inspectable file, upload it to the current issue before final disposition. Local filesystem paths are not enough because board users, reviewers, and cloud operators may not have access to the agent workspace.
|
|
100
|
+
|
|
101
|
+
For technical upload instructions, read `references/artifacts.md`.
|
|
102
|
+
|
|
97
103
|
**Step 8 — Update status and communicate.** Always include the run ID header.
|
|
98
104
|
If you are blocked at any point, you MUST update the issue to `blocked` before exiting the heartbeat, with a comment that explains the blocker and who needs to act.
|
|
99
105
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Generated Artifacts and Work Products
|
|
2
|
+
|
|
3
|
+
When work produces a user-inspectable file, upload it to the current issue before final disposition. Local filesystem paths are not enough because board users, reviewers, and cloud operators may not have access to the agent workspace.
|
|
4
|
+
|
|
5
|
+
Use the helper bundled with this skill. From an installed `paperclip` skill directory, the helper lives at `scripts/paperclip-upload-artifact.sh`:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
scripts/paperclip-upload-artifact.sh path/to/output.webm \
|
|
9
|
+
--title "Walkthrough render" \
|
|
10
|
+
--summary "Rendered walkthrough for review"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The helper uses `PAPERCLIP_API_URL`, `PAPERCLIP_API_KEY`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_TASK_ID`, and `PAPERCLIP_RUN_ID`. It uploads the file as an issue attachment, creates an attachment-backed artifact work product by default, and prints issue-safe markdown links for your final comment.
|
|
14
|
+
|
|
15
|
+
If the helper is unavailable, use the Paperclip API directly:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
curl -sS -X POST \
|
|
19
|
+
"$PAPERCLIP_API_URL/api/companies/$PAPERCLIP_COMPANY_ID/issues/$PAPERCLIP_TASK_ID/attachments" \
|
|
20
|
+
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
|
21
|
+
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
|
22
|
+
-F 'file=@"path/to/output.webm";type=video/webm'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then create a work product when the file is the deliverable. The server canonicalizes attachment-backed artifact metadata from the `attachmentId`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -sS -X POST \
|
|
29
|
+
"$PAPERCLIP_API_URL/api/issues/$PAPERCLIP_TASK_ID/work-products" \
|
|
30
|
+
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
|
31
|
+
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
|
32
|
+
-H "Content-Type: application/json" \
|
|
33
|
+
--data-binary '{
|
|
34
|
+
"type": "artifact",
|
|
35
|
+
"provider": "paperclip",
|
|
36
|
+
"title": "Walkthrough render",
|
|
37
|
+
"status": "ready_for_review",
|
|
38
|
+
"reviewState": "needs_board_review",
|
|
39
|
+
"isPrimary": true,
|
|
40
|
+
"metadata": { "attachmentId": "<uploaded-attachment-id>" }
|
|
41
|
+
}'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
In your final issue comment, link the uploaded attachment or work product and describe what it contains. Do not leave artifact-producing work `in_progress` with only a local path or a `Remaining` note.
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
usage() {
|
|
6
|
+
cat <<'EOF'
|
|
7
|
+
Usage:
|
|
8
|
+
paperclip-upload-artifact.sh FILE [options]
|
|
9
|
+
|
|
10
|
+
Uploads a generated file from the current workspace to the current Paperclip
|
|
11
|
+
issue, then creates an attachment-backed artifact work product by default.
|
|
12
|
+
|
|
13
|
+
Required environment for live uploads:
|
|
14
|
+
PAPERCLIP_API_URL, PAPERCLIP_API_KEY, PAPERCLIP_COMPANY_ID, PAPERCLIP_TASK_ID, PAPERCLIP_RUN_ID
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--issue-id ID Issue id to attach to (default: PAPERCLIP_TASK_ID)
|
|
18
|
+
--company-id ID Company id (default: PAPERCLIP_COMPANY_ID)
|
|
19
|
+
--title TEXT Work product title (default: file basename)
|
|
20
|
+
--summary TEXT Work product summary
|
|
21
|
+
--content-type TYPE Override detected upload content type
|
|
22
|
+
--status STATUS Work product status (default: ready_for_review)
|
|
23
|
+
--no-work-product Only upload the issue attachment
|
|
24
|
+
--no-primary Do not mark the artifact work product primary for its type
|
|
25
|
+
--output FORMAT markdown or json (default: markdown)
|
|
26
|
+
--dry-run Print resolved upload settings without calling the API
|
|
27
|
+
--help, -h Show this help
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
scripts/paperclip-upload-artifact.sh dist/demo.mp4 \
|
|
31
|
+
--title "Demo video render" \
|
|
32
|
+
--summary "MP4 render for board review"
|
|
33
|
+
|
|
34
|
+
scripts/paperclip-upload-artifact.sh out/walkthrough.webm \
|
|
35
|
+
--title "Walkthrough video" \
|
|
36
|
+
--content-type video/webm
|
|
37
|
+
EOF
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
require_command() {
|
|
41
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
42
|
+
printf 'Missing required command: %s\n' "$1" >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
json_bool() {
|
|
48
|
+
if [[ "${1:-0}" == "1" ]]; then
|
|
49
|
+
printf 'true'
|
|
50
|
+
else
|
|
51
|
+
printf 'false'
|
|
52
|
+
fi
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
detect_content_type() {
|
|
56
|
+
local path="$1"
|
|
57
|
+
local lower
|
|
58
|
+
lower="$(printf '%s' "$path" | tr '[:upper:]' '[:lower:]')"
|
|
59
|
+
|
|
60
|
+
case "$lower" in
|
|
61
|
+
*.mp4|*.m4v) printf 'video/mp4' ;;
|
|
62
|
+
*.webm) printf 'video/webm' ;;
|
|
63
|
+
*.mov|*.qt) printf 'video/quicktime' ;;
|
|
64
|
+
*.png) printf 'image/png' ;;
|
|
65
|
+
*.jpg|*.jpeg) printf 'image/jpeg' ;;
|
|
66
|
+
*.gif) printf 'image/gif' ;;
|
|
67
|
+
*.webp) printf 'image/webp' ;;
|
|
68
|
+
*.svg) printf 'image/svg+xml' ;;
|
|
69
|
+
*.pdf) printf 'application/pdf' ;;
|
|
70
|
+
*.txt|*.log) printf 'text/plain' ;;
|
|
71
|
+
*.md|*.markdown) printf 'text/markdown' ;;
|
|
72
|
+
*.json) printf 'application/json' ;;
|
|
73
|
+
*.csv) printf 'text/csv' ;;
|
|
74
|
+
*.html|*.htm) printf 'text/html' ;;
|
|
75
|
+
*.zip) printf 'application/zip' ;;
|
|
76
|
+
*)
|
|
77
|
+
if command -v file >/dev/null 2>&1; then
|
|
78
|
+
file --brief --mime-type "$path"
|
|
79
|
+
else
|
|
80
|
+
printf 'application/octet-stream'
|
|
81
|
+
fi
|
|
82
|
+
;;
|
|
83
|
+
esac
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
request_json() {
|
|
87
|
+
local method="$1"
|
|
88
|
+
local url="$2"
|
|
89
|
+
local body="${3:-}"
|
|
90
|
+
local response_file
|
|
91
|
+
local status_code
|
|
92
|
+
|
|
93
|
+
response_file="$(mktemp)"
|
|
94
|
+
if [[ -n "$body" ]]; then
|
|
95
|
+
status_code="$(
|
|
96
|
+
curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \
|
|
97
|
+
"$url" \
|
|
98
|
+
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
|
99
|
+
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
|
100
|
+
-H 'Content-Type: application/json' \
|
|
101
|
+
--data-binary "$body"
|
|
102
|
+
)"
|
|
103
|
+
else
|
|
104
|
+
status_code="$(
|
|
105
|
+
curl -sS -X "$method" -w '%{http_code}' -o "$response_file" \
|
|
106
|
+
"$url" \
|
|
107
|
+
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
|
108
|
+
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID"
|
|
109
|
+
)"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then
|
|
113
|
+
printf 'Request failed (%s): %s\n' "$status_code" "$url" >&2
|
|
114
|
+
cat "$response_file" >&2
|
|
115
|
+
printf '\n' >&2
|
|
116
|
+
rm -f "$response_file"
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
cat "$response_file"
|
|
121
|
+
rm -f "$response_file"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
upload_file() {
|
|
125
|
+
local url="$1"
|
|
126
|
+
local path="$2"
|
|
127
|
+
local content_type="$3"
|
|
128
|
+
local escaped_path
|
|
129
|
+
local response_file
|
|
130
|
+
local status_code
|
|
131
|
+
|
|
132
|
+
escaped_path="${path//\\/\\\\}"
|
|
133
|
+
escaped_path="${escaped_path//\"/\\\"}"
|
|
134
|
+
response_file="$(mktemp)"
|
|
135
|
+
status_code="$(
|
|
136
|
+
curl -sS -X POST -w '%{http_code}' -o "$response_file" \
|
|
137
|
+
"$url" \
|
|
138
|
+
-H "Authorization: Bearer $PAPERCLIP_API_KEY" \
|
|
139
|
+
-H "X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID" \
|
|
140
|
+
-F "file=@\"${escaped_path}\";type=${content_type}"
|
|
141
|
+
)"
|
|
142
|
+
|
|
143
|
+
if [[ "$status_code" -lt 200 || "$status_code" -ge 300 ]]; then
|
|
144
|
+
printf 'Upload failed (%s): %s\n' "$status_code" "$url" >&2
|
|
145
|
+
cat "$response_file" >&2
|
|
146
|
+
printf '\n' >&2
|
|
147
|
+
rm -f "$response_file"
|
|
148
|
+
exit 1
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
cat "$response_file"
|
|
152
|
+
rm -f "$response_file"
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
file_path=""
|
|
156
|
+
issue_id="${PAPERCLIP_TASK_ID:-}"
|
|
157
|
+
company_id="${PAPERCLIP_COMPANY_ID:-}"
|
|
158
|
+
title=""
|
|
159
|
+
summary=""
|
|
160
|
+
content_type=""
|
|
161
|
+
status="ready_for_review"
|
|
162
|
+
create_work_product=1
|
|
163
|
+
is_primary=1
|
|
164
|
+
output_format="markdown"
|
|
165
|
+
dry_run=0
|
|
166
|
+
|
|
167
|
+
while [[ $# -gt 0 ]]; do
|
|
168
|
+
case "$1" in
|
|
169
|
+
--issue-id)
|
|
170
|
+
issue_id="${2:-}"
|
|
171
|
+
shift 2
|
|
172
|
+
;;
|
|
173
|
+
--company-id)
|
|
174
|
+
company_id="${2:-}"
|
|
175
|
+
shift 2
|
|
176
|
+
;;
|
|
177
|
+
--title)
|
|
178
|
+
title="${2:-}"
|
|
179
|
+
shift 2
|
|
180
|
+
;;
|
|
181
|
+
--summary)
|
|
182
|
+
summary="${2:-}"
|
|
183
|
+
shift 2
|
|
184
|
+
;;
|
|
185
|
+
--content-type)
|
|
186
|
+
content_type="${2:-}"
|
|
187
|
+
shift 2
|
|
188
|
+
;;
|
|
189
|
+
--status)
|
|
190
|
+
status="${2:-}"
|
|
191
|
+
shift 2
|
|
192
|
+
;;
|
|
193
|
+
--no-work-product)
|
|
194
|
+
create_work_product=0
|
|
195
|
+
shift
|
|
196
|
+
;;
|
|
197
|
+
--no-primary)
|
|
198
|
+
is_primary=0
|
|
199
|
+
shift
|
|
200
|
+
;;
|
|
201
|
+
--output)
|
|
202
|
+
output_format="${2:-}"
|
|
203
|
+
shift 2
|
|
204
|
+
;;
|
|
205
|
+
--dry-run)
|
|
206
|
+
dry_run=1
|
|
207
|
+
shift
|
|
208
|
+
;;
|
|
209
|
+
--help|-h)
|
|
210
|
+
usage
|
|
211
|
+
exit 0
|
|
212
|
+
;;
|
|
213
|
+
--*)
|
|
214
|
+
printf 'Unknown argument: %s\n' "$1" >&2
|
|
215
|
+
usage >&2
|
|
216
|
+
exit 1
|
|
217
|
+
;;
|
|
218
|
+
*)
|
|
219
|
+
if [[ -n "$file_path" ]]; then
|
|
220
|
+
printf 'Unexpected positional argument: %s\n' "$1" >&2
|
|
221
|
+
usage >&2
|
|
222
|
+
exit 1
|
|
223
|
+
fi
|
|
224
|
+
file_path="$1"
|
|
225
|
+
shift
|
|
226
|
+
;;
|
|
227
|
+
esac
|
|
228
|
+
done
|
|
229
|
+
|
|
230
|
+
if [[ -z "$file_path" ]]; then
|
|
231
|
+
printf 'Missing file path.\n' >&2
|
|
232
|
+
usage >&2
|
|
233
|
+
exit 1
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
if [[ ! -f "$file_path" ]]; then
|
|
237
|
+
printf 'Artifact file does not exist: %s\n' "$file_path" >&2
|
|
238
|
+
exit 1
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
if [[ "$output_format" != "markdown" && "$output_format" != "json" ]]; then
|
|
242
|
+
printf 'Unsupported output format: %s\n' "$output_format" >&2
|
|
243
|
+
exit 1
|
|
244
|
+
fi
|
|
245
|
+
|
|
246
|
+
require_command curl
|
|
247
|
+
require_command jq
|
|
248
|
+
|
|
249
|
+
if [[ -z "$title" ]]; then
|
|
250
|
+
title="$(basename "$file_path")"
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
if [[ -z "$content_type" ]]; then
|
|
254
|
+
content_type="$(detect_content_type "$file_path")"
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
if [[ "$dry_run" == "1" ]]; then
|
|
258
|
+
create_work_product_json="$(json_bool "$create_work_product")"
|
|
259
|
+
is_primary_json="$(json_bool "$is_primary")"
|
|
260
|
+
jq -n \
|
|
261
|
+
--arg file "$file_path" \
|
|
262
|
+
--arg issueId "$issue_id" \
|
|
263
|
+
--arg companyId "$company_id" \
|
|
264
|
+
--arg title "$title" \
|
|
265
|
+
--arg summary "$summary" \
|
|
266
|
+
--arg contentType "$content_type" \
|
|
267
|
+
--arg status "$status" \
|
|
268
|
+
--argjson createWorkProduct "$create_work_product_json" \
|
|
269
|
+
--argjson isPrimary "$is_primary_json" \
|
|
270
|
+
'{file: $file, issueId: $issueId, companyId: $companyId, title: $title, summary: $summary, contentType: $contentType, status: $status, createWorkProduct: $createWorkProduct, isPrimary: $isPrimary}'
|
|
271
|
+
exit 0
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
if [[ -z "${PAPERCLIP_API_URL:-}" || -z "${PAPERCLIP_API_KEY:-}" || -z "${PAPERCLIP_RUN_ID:-}" ]]; then
|
|
275
|
+
printf 'Missing PAPERCLIP_API_URL, PAPERCLIP_API_KEY, or PAPERCLIP_RUN_ID.\n' >&2
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
if [[ -z "$issue_id" || -z "$company_id" ]]; then
|
|
280
|
+
printf 'Missing issue or company id. Pass --issue-id/--company-id or set PAPERCLIP_TASK_ID/PAPERCLIP_COMPANY_ID.\n' >&2
|
|
281
|
+
exit 1
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
api_base="${PAPERCLIP_API_URL%/}/api"
|
|
285
|
+
attachment="$(
|
|
286
|
+
upload_file \
|
|
287
|
+
"$api_base/companies/$company_id/issues/$issue_id/attachments" \
|
|
288
|
+
"$file_path" \
|
|
289
|
+
"$content_type"
|
|
290
|
+
)"
|
|
291
|
+
|
|
292
|
+
work_product="null"
|
|
293
|
+
if [[ "$create_work_product" == "1" ]]; then
|
|
294
|
+
is_primary_json="$(json_bool "$is_primary")"
|
|
295
|
+
attachment_id="$(jq -r '.id // empty' <<<"$attachment")"
|
|
296
|
+
byte_size="$(jq -r '.byteSize // 0' <<<"$attachment")"
|
|
297
|
+
content_path="$(jq -r '.contentPath // empty' <<<"$attachment")"
|
|
298
|
+
open_path="$(jq -r '.openPath // .contentPath // empty' <<<"$attachment")"
|
|
299
|
+
download_path="$(jq -r '.downloadPath // (if .contentPath then (.contentPath + "?download=1") else "" end)' <<<"$attachment")"
|
|
300
|
+
original_filename="$(jq -r '.originalFilename // empty' <<<"$attachment")"
|
|
301
|
+
|
|
302
|
+
if [[ -z "$attachment_id" || -z "$content_path" || -z "$download_path" ]]; then
|
|
303
|
+
printf 'Upload response did not include attachment path metadata.\n' >&2
|
|
304
|
+
printf '%s\n' "$attachment" >&2
|
|
305
|
+
exit 1
|
|
306
|
+
fi
|
|
307
|
+
|
|
308
|
+
work_product_payload="$(
|
|
309
|
+
jq -nc \
|
|
310
|
+
--arg title "$title" \
|
|
311
|
+
--arg summary "$summary" \
|
|
312
|
+
--arg status "$status" \
|
|
313
|
+
--arg runId "$PAPERCLIP_RUN_ID" \
|
|
314
|
+
--arg attachmentId "$attachment_id" \
|
|
315
|
+
--arg contentType "$content_type" \
|
|
316
|
+
--argjson byteSize "$byte_size" \
|
|
317
|
+
--arg contentPath "$content_path" \
|
|
318
|
+
--arg openPath "$open_path" \
|
|
319
|
+
--arg downloadPath "$download_path" \
|
|
320
|
+
--arg originalFilename "$original_filename" \
|
|
321
|
+
--argjson isPrimary "$is_primary_json" \
|
|
322
|
+
'{
|
|
323
|
+
type: "artifact",
|
|
324
|
+
provider: "paperclip",
|
|
325
|
+
title: $title,
|
|
326
|
+
status: $status,
|
|
327
|
+
reviewState: "none",
|
|
328
|
+
isPrimary: $isPrimary,
|
|
329
|
+
healthStatus: "unknown",
|
|
330
|
+
summary: (if $summary == "" then null else $summary end),
|
|
331
|
+
createdByRunId: $runId,
|
|
332
|
+
metadata: {
|
|
333
|
+
attachmentId: $attachmentId,
|
|
334
|
+
contentType: $contentType,
|
|
335
|
+
byteSize: $byteSize,
|
|
336
|
+
contentPath: $contentPath,
|
|
337
|
+
openPath: $openPath,
|
|
338
|
+
downloadPath: $downloadPath,
|
|
339
|
+
originalFilename: (if $originalFilename == "" then null else $originalFilename end)
|
|
340
|
+
}
|
|
341
|
+
}'
|
|
342
|
+
)"
|
|
343
|
+
|
|
344
|
+
work_product="$(
|
|
345
|
+
request_json \
|
|
346
|
+
POST \
|
|
347
|
+
"$api_base/issues/$issue_id/work-products" \
|
|
348
|
+
"$work_product_payload"
|
|
349
|
+
)"
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
if [[ "$output_format" == "json" ]]; then
|
|
353
|
+
jq -n --argjson attachment "$attachment" --argjson workProduct "$work_product" \
|
|
354
|
+
'{attachment: $attachment, workProduct: $workProduct}'
|
|
355
|
+
exit 0
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
content_path="$(jq -r '.contentPath // empty' <<<"$attachment")"
|
|
359
|
+
download_path="$(jq -r '.downloadPath // (if .contentPath then (.contentPath + "?download=1") else "" end)' <<<"$attachment")"
|
|
360
|
+
attachment_id="$(jq -r '.id // empty' <<<"$attachment")"
|
|
361
|
+
work_product_id="$(jq -r '.id // empty' <<<"$work_product")"
|
|
362
|
+
|
|
363
|
+
printf 'Uploaded artifact\n\n'
|
|
364
|
+
printf -- '- Attachment: [%s](%s)\n' "$title" "$content_path"
|
|
365
|
+
printf -- '- Download: [%s](%s)\n' "$title" "$download_path"
|
|
366
|
+
printf -- '- Attachment ID: `%s`\n' "$attachment_id"
|
|
367
|
+
if [[ -n "$work_product_id" ]]; then
|
|
368
|
+
printf -- '- Work product ID: `%s`\n' "$work_product_id"
|
|
369
|
+
fi
|
|
370
|
+
printf '\nFinal comment snippet:\n\n'
|
|
371
|
+
printf -- '- Artifact: [%s](%s)\n' "$title" "$content_path"
|