@acnlabs/paperclip-plugin-acn 0.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/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/constants.d.ts +17 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +17 -0
- package/dist/constants.js.map +1 -0
- package/dist/lib/echo-guard.d.ts +28 -0
- package/dist/lib/echo-guard.d.ts.map +1 -0
- package/dist/lib/echo-guard.js +40 -0
- package/dist/lib/echo-guard.js.map +1 -0
- package/dist/lib/secrets.d.ts +28 -0
- package/dist/lib/secrets.d.ts.map +1 -0
- package/dist/lib/secrets.js +33 -0
- package/dist/lib/secrets.js.map +1 -0
- package/dist/lib/signature.d.ts +26 -0
- package/dist/lib/signature.d.ts.map +1 -0
- package/dist/lib/signature.js +45 -0
- package/dist/lib/signature.js.map +1 -0
- package/dist/manifest.d.ts +4 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +97 -0
- package/dist/manifest.js.map +1 -0
- package/dist/ui/index.d.ts +19 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +287 -0
- package/dist/ui/index.js.map +7 -0
- package/dist/worker.d.ts +27 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +491 -0
- package/dist/worker.js.map +1 -0
- package/package.json +61 -0
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// src/ui/index.tsx
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
usePluginAction,
|
|
5
|
+
usePluginData
|
|
6
|
+
} from "@paperclipai/plugin-sdk/ui";
|
|
7
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var styles = {
|
|
9
|
+
container: {
|
|
10
|
+
padding: "16px",
|
|
11
|
+
fontFamily: "inherit",
|
|
12
|
+
fontSize: "13px",
|
|
13
|
+
color: "var(--color-fg-default, #1a1a1a)"
|
|
14
|
+
},
|
|
15
|
+
row: {
|
|
16
|
+
display: "flex",
|
|
17
|
+
gap: "8px",
|
|
18
|
+
alignItems: "baseline",
|
|
19
|
+
marginBottom: "6px"
|
|
20
|
+
},
|
|
21
|
+
label: {
|
|
22
|
+
fontWeight: 600,
|
|
23
|
+
color: "var(--color-fg-muted, #666)",
|
|
24
|
+
minWidth: "100px",
|
|
25
|
+
flexShrink: 0
|
|
26
|
+
},
|
|
27
|
+
badge: (color) => ({
|
|
28
|
+
display: "inline-block",
|
|
29
|
+
padding: "2px 8px",
|
|
30
|
+
borderRadius: "12px",
|
|
31
|
+
fontSize: "11px",
|
|
32
|
+
fontWeight: 600,
|
|
33
|
+
background: color,
|
|
34
|
+
color: "#fff",
|
|
35
|
+
textTransform: "uppercase",
|
|
36
|
+
letterSpacing: "0.04em"
|
|
37
|
+
}),
|
|
38
|
+
section: {
|
|
39
|
+
marginTop: "16px",
|
|
40
|
+
borderTop: "1px solid var(--color-border-default, #e5e5e5)",
|
|
41
|
+
paddingTop: "12px"
|
|
42
|
+
},
|
|
43
|
+
sectionTitle: {
|
|
44
|
+
fontWeight: 700,
|
|
45
|
+
marginBottom: "8px",
|
|
46
|
+
fontSize: "12px",
|
|
47
|
+
textTransform: "uppercase",
|
|
48
|
+
letterSpacing: "0.06em",
|
|
49
|
+
color: "var(--color-fg-muted, #888)"
|
|
50
|
+
},
|
|
51
|
+
participationCard: {
|
|
52
|
+
background: "var(--color-bg-subtle, #f6f6f6)",
|
|
53
|
+
borderRadius: "6px",
|
|
54
|
+
padding: "10px 12px",
|
|
55
|
+
marginBottom: "8px"
|
|
56
|
+
},
|
|
57
|
+
pre: {
|
|
58
|
+
background: "var(--color-bg-subtle, #f0f0f0)",
|
|
59
|
+
borderRadius: "4px",
|
|
60
|
+
padding: "8px",
|
|
61
|
+
fontSize: "12px",
|
|
62
|
+
overflowX: "auto",
|
|
63
|
+
whiteSpace: "pre-wrap",
|
|
64
|
+
wordBreak: "break-word",
|
|
65
|
+
marginTop: "6px"
|
|
66
|
+
},
|
|
67
|
+
actionRow: {
|
|
68
|
+
display: "flex",
|
|
69
|
+
gap: "8px",
|
|
70
|
+
marginTop: "16px"
|
|
71
|
+
},
|
|
72
|
+
btn: (variant) => ({
|
|
73
|
+
padding: "6px 16px",
|
|
74
|
+
borderRadius: "6px",
|
|
75
|
+
border: "none",
|
|
76
|
+
cursor: "pointer",
|
|
77
|
+
fontWeight: 600,
|
|
78
|
+
fontSize: "13px",
|
|
79
|
+
background: variant === "approve" ? "#16a34a" : variant === "reject" ? "#dc2626" : "var(--color-bg-subtle, #e5e5e5)",
|
|
80
|
+
color: variant === "neutral" ? "var(--color-fg-default, #333)" : "#fff",
|
|
81
|
+
opacity: 1,
|
|
82
|
+
transition: "opacity 0.15s"
|
|
83
|
+
}),
|
|
84
|
+
textarea: {
|
|
85
|
+
width: "100%",
|
|
86
|
+
minHeight: "64px",
|
|
87
|
+
borderRadius: "6px",
|
|
88
|
+
border: "1px solid var(--color-border-default, #ccc)",
|
|
89
|
+
padding: "6px 8px",
|
|
90
|
+
fontSize: "12px",
|
|
91
|
+
fontFamily: "inherit",
|
|
92
|
+
resize: "vertical",
|
|
93
|
+
boxSizing: "border-box",
|
|
94
|
+
marginTop: "8px"
|
|
95
|
+
},
|
|
96
|
+
muted: {
|
|
97
|
+
color: "var(--color-fg-muted, #888)",
|
|
98
|
+
fontSize: "12px"
|
|
99
|
+
},
|
|
100
|
+
mono: {
|
|
101
|
+
fontFamily: "monospace",
|
|
102
|
+
fontSize: "11px",
|
|
103
|
+
background: "var(--color-bg-subtle, #f0f0f0)",
|
|
104
|
+
padding: "1px 5px",
|
|
105
|
+
borderRadius: "3px"
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var STATUS_COLORS = {
|
|
109
|
+
open: "#2563eb",
|
|
110
|
+
in_progress: "#d97706",
|
|
111
|
+
in_review: "#7c3aed",
|
|
112
|
+
completed: "#16a34a",
|
|
113
|
+
cancelled: "#6b7280",
|
|
114
|
+
rejected: "#dc2626",
|
|
115
|
+
submitted: "#7c3aed",
|
|
116
|
+
accepted: "#d97706"
|
|
117
|
+
};
|
|
118
|
+
function StatusBadge({ status }) {
|
|
119
|
+
const color = STATUS_COLORS[status] ?? "#6b7280";
|
|
120
|
+
return /* @__PURE__ */ jsx("span", { style: styles.badge(color), children: status.replace(/_/g, " ") });
|
|
121
|
+
}
|
|
122
|
+
function ParticipationItem({
|
|
123
|
+
p
|
|
124
|
+
}) {
|
|
125
|
+
const [expanded, setExpanded] = useState(false);
|
|
126
|
+
const hasSubmission = Boolean(p.submission_content);
|
|
127
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.participationCard, children: [
|
|
128
|
+
/* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
129
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: "Agent" }),
|
|
130
|
+
/* @__PURE__ */ jsx("span", { style: styles.mono, children: p.agent_id })
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
133
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: "Status" }),
|
|
134
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: p.status }),
|
|
135
|
+
p.resubmit_count > 0 && /* @__PURE__ */ jsxs("span", { style: styles.muted, children: [
|
|
136
|
+
"(",
|
|
137
|
+
p.resubmit_count,
|
|
138
|
+
" resubmit",
|
|
139
|
+
p.resubmit_count > 1 ? "s" : "",
|
|
140
|
+
")"
|
|
141
|
+
] })
|
|
142
|
+
] }),
|
|
143
|
+
p.submitted_at && /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
144
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: "Submitted" }),
|
|
145
|
+
/* @__PURE__ */ jsx("span", { style: styles.muted, children: new Date(p.submitted_at).toLocaleString() })
|
|
146
|
+
] }),
|
|
147
|
+
hasSubmission && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
148
|
+
/* @__PURE__ */ jsx(
|
|
149
|
+
"button",
|
|
150
|
+
{
|
|
151
|
+
type: "button",
|
|
152
|
+
style: { ...styles.btn("neutral"), marginTop: "6px", fontSize: "11px", padding: "3px 10px" },
|
|
153
|
+
onClick: () => setExpanded((v) => !v),
|
|
154
|
+
children: expanded ? "Hide submission" : "Show submission"
|
|
155
|
+
}
|
|
156
|
+
),
|
|
157
|
+
expanded && /* @__PURE__ */ jsx("pre", { style: styles.pre, children: p.submission_content })
|
|
158
|
+
] })
|
|
159
|
+
] });
|
|
160
|
+
}
|
|
161
|
+
function ReviewPanel({
|
|
162
|
+
taskId,
|
|
163
|
+
onDone
|
|
164
|
+
}) {
|
|
165
|
+
const [feedback, setFeedback] = useState("");
|
|
166
|
+
const [pending, setPending] = useState(null);
|
|
167
|
+
const [error, setError] = useState(null);
|
|
168
|
+
const review = usePluginAction("acn-review");
|
|
169
|
+
async function handleReview(approved) {
|
|
170
|
+
const action = approved ? "approve" : "reject";
|
|
171
|
+
setPending(action);
|
|
172
|
+
setError(null);
|
|
173
|
+
try {
|
|
174
|
+
await review({ taskId, approved, feedback: feedback.trim() || void 0 });
|
|
175
|
+
onDone();
|
|
176
|
+
} catch (err) {
|
|
177
|
+
setError(err instanceof Error ? err.message : "Review failed");
|
|
178
|
+
} finally {
|
|
179
|
+
setPending(null);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.section, children: [
|
|
183
|
+
/* @__PURE__ */ jsx("div", { style: styles.sectionTitle, children: "Review Submission" }),
|
|
184
|
+
/* @__PURE__ */ jsx(
|
|
185
|
+
"textarea",
|
|
186
|
+
{
|
|
187
|
+
style: styles.textarea,
|
|
188
|
+
placeholder: "Optional feedback\u2026",
|
|
189
|
+
value: feedback,
|
|
190
|
+
onChange: (e) => setFeedback(e.target.value),
|
|
191
|
+
disabled: pending !== null
|
|
192
|
+
}
|
|
193
|
+
),
|
|
194
|
+
error && /* @__PURE__ */ jsx("div", { style: { color: "#dc2626", fontSize: "12px", marginTop: "4px" }, children: error }),
|
|
195
|
+
/* @__PURE__ */ jsxs("div", { style: styles.actionRow, children: [
|
|
196
|
+
/* @__PURE__ */ jsx(
|
|
197
|
+
"button",
|
|
198
|
+
{
|
|
199
|
+
type: "button",
|
|
200
|
+
style: { ...styles.btn("approve"), opacity: pending ? 0.6 : 1 },
|
|
201
|
+
disabled: pending !== null,
|
|
202
|
+
onClick: () => void handleReview(true),
|
|
203
|
+
children: pending === "approve" ? "Approving\u2026" : "Approve"
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
/* @__PURE__ */ jsx(
|
|
207
|
+
"button",
|
|
208
|
+
{
|
|
209
|
+
type: "button",
|
|
210
|
+
style: { ...styles.btn("reject"), opacity: pending ? 0.6 : 1 },
|
|
211
|
+
disabled: pending !== null,
|
|
212
|
+
onClick: () => void handleReview(false),
|
|
213
|
+
children: pending === "reject" ? "Rejecting\u2026" : "Reject"
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
] })
|
|
217
|
+
] });
|
|
218
|
+
}
|
|
219
|
+
function ACNIssueTab({ context }) {
|
|
220
|
+
const issueId = context.entityId;
|
|
221
|
+
const companyId = context.companyId;
|
|
222
|
+
const [reviewDone, setReviewDone] = useState(false);
|
|
223
|
+
const { data, loading, error } = usePluginData(
|
|
224
|
+
"acn-task-info",
|
|
225
|
+
issueId && companyId ? { issueId, companyId } : {}
|
|
226
|
+
);
|
|
227
|
+
if (!issueId) {
|
|
228
|
+
return /* @__PURE__ */ jsx("div", { style: styles.container, children: /* @__PURE__ */ jsx("span", { style: styles.muted, children: "No issue selected." }) });
|
|
229
|
+
}
|
|
230
|
+
if (loading) {
|
|
231
|
+
return /* @__PURE__ */ jsx("div", { style: styles.container, children: /* @__PURE__ */ jsx("span", { style: styles.muted, children: "Loading\u2026" }) });
|
|
232
|
+
}
|
|
233
|
+
if (error) {
|
|
234
|
+
return /* @__PURE__ */ jsx("div", { style: styles.container, children: /* @__PURE__ */ jsxs("span", { style: { color: "#dc2626" }, children: [
|
|
235
|
+
"Failed to load ACN data: ",
|
|
236
|
+
error.message
|
|
237
|
+
] }) });
|
|
238
|
+
}
|
|
239
|
+
if (!data) {
|
|
240
|
+
return /* @__PURE__ */ jsx("div", { style: styles.container, children: /* @__PURE__ */ jsx("span", { style: styles.muted, children: "This issue is not linked to an ACN task." }) });
|
|
241
|
+
}
|
|
242
|
+
const submittedParticipation = data.participations.find(
|
|
243
|
+
(p) => p.status === "submitted"
|
|
244
|
+
);
|
|
245
|
+
const canReview = !reviewDone && Boolean(submittedParticipation);
|
|
246
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
|
|
247
|
+
/* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
248
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: "ACN Task" }),
|
|
249
|
+
/* @__PURE__ */ jsx("span", { style: styles.mono, children: data.task_id })
|
|
250
|
+
] }),
|
|
251
|
+
/* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
252
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: "Status" }),
|
|
253
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: data.status })
|
|
254
|
+
] }),
|
|
255
|
+
parseFloat(data.reward ?? "0") > 0 && /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
256
|
+
/* @__PURE__ */ jsx("span", { style: styles.label, children: "Reward" }),
|
|
257
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
258
|
+
data.reward,
|
|
259
|
+
" ",
|
|
260
|
+
data.reward_currency
|
|
261
|
+
] })
|
|
262
|
+
] }),
|
|
263
|
+
data.participations.length > 0 && /* @__PURE__ */ jsxs("div", { style: styles.section, children: [
|
|
264
|
+
/* @__PURE__ */ jsxs("div", { style: styles.sectionTitle, children: [
|
|
265
|
+
"Participants (",
|
|
266
|
+
data.participations.length,
|
|
267
|
+
")"
|
|
268
|
+
] }),
|
|
269
|
+
data.participations.map((p) => /* @__PURE__ */ jsx(ParticipationItem, { p }, p.participation_id))
|
|
270
|
+
] }),
|
|
271
|
+
canReview && /* @__PURE__ */ jsx(
|
|
272
|
+
ReviewPanel,
|
|
273
|
+
{
|
|
274
|
+
taskId: data.task_id,
|
|
275
|
+
onDone: () => {
|
|
276
|
+
setReviewDone(true);
|
|
277
|
+
setTimeout(() => setReviewDone(false), 3e3);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
),
|
|
281
|
+
reviewDone && /* @__PURE__ */ jsx("div", { style: { ...styles.section, color: "#16a34a", fontWeight: 600 }, children: "Review submitted. ACN will process the result." })
|
|
282
|
+
] });
|
|
283
|
+
}
|
|
284
|
+
export {
|
|
285
|
+
ACNIssueTab
|
|
286
|
+
};
|
|
287
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/index.tsx"],
|
|
4
|
+
"sourcesContent": ["import React, { useState } from \"react\";\nimport {\n usePluginAction,\n usePluginData,\n type PluginDetailTabProps,\n} from \"@paperclipai/plugin-sdk/ui\";\n\n// \u2500\u2500 Types shared with worker bridge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface AcnTaskInfo {\n task_id: string;\n title: string;\n status: string;\n /** Decimal string from ACN backend (e.g. \"10.00\"). */\n reward: string;\n reward_currency: string;\n participations: Array<{\n participation_id: string;\n agent_id: string;\n status: string;\n submission_content: string | null;\n submitted_at: string | null;\n resubmit_count: number;\n }>;\n}\n\n// \u2500\u2500 Minimal inline styles (no Tailwind dependency) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst styles = {\n container: {\n padding: \"16px\",\n fontFamily: \"inherit\",\n fontSize: \"13px\",\n color: \"var(--color-fg-default, #1a1a1a)\",\n } as React.CSSProperties,\n\n row: {\n display: \"flex\",\n gap: \"8px\",\n alignItems: \"baseline\",\n marginBottom: \"6px\",\n } as React.CSSProperties,\n\n label: {\n fontWeight: 600,\n color: \"var(--color-fg-muted, #666)\",\n minWidth: \"100px\",\n flexShrink: 0,\n } as React.CSSProperties,\n\n badge: (color: string): React.CSSProperties => ({\n display: \"inline-block\",\n padding: \"2px 8px\",\n borderRadius: \"12px\",\n fontSize: \"11px\",\n fontWeight: 600,\n background: color,\n color: \"#fff\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.04em\",\n }),\n\n section: {\n marginTop: \"16px\",\n borderTop: \"1px solid var(--color-border-default, #e5e5e5)\",\n paddingTop: \"12px\",\n } as React.CSSProperties,\n\n sectionTitle: {\n fontWeight: 700,\n marginBottom: \"8px\",\n fontSize: \"12px\",\n textTransform: \"uppercase\",\n letterSpacing: \"0.06em\",\n color: \"var(--color-fg-muted, #888)\",\n } as React.CSSProperties,\n\n participationCard: {\n background: \"var(--color-bg-subtle, #f6f6f6)\",\n borderRadius: \"6px\",\n padding: \"10px 12px\",\n marginBottom: \"8px\",\n } as React.CSSProperties,\n\n pre: {\n background: \"var(--color-bg-subtle, #f0f0f0)\",\n borderRadius: \"4px\",\n padding: \"8px\",\n fontSize: \"12px\",\n overflowX: \"auto\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n marginTop: \"6px\",\n } as React.CSSProperties,\n\n actionRow: {\n display: \"flex\",\n gap: \"8px\",\n marginTop: \"16px\",\n } as React.CSSProperties,\n\n btn: (variant: \"approve\" | \"reject\" | \"neutral\"): React.CSSProperties => ({\n padding: \"6px 16px\",\n borderRadius: \"6px\",\n border: \"none\",\n cursor: \"pointer\",\n fontWeight: 600,\n fontSize: \"13px\",\n background:\n variant === \"approve\"\n ? \"#16a34a\"\n : variant === \"reject\"\n ? \"#dc2626\"\n : \"var(--color-bg-subtle, #e5e5e5)\",\n color: variant === \"neutral\" ? \"var(--color-fg-default, #333)\" : \"#fff\",\n opacity: 1,\n transition: \"opacity 0.15s\",\n }),\n\n textarea: {\n width: \"100%\",\n minHeight: \"64px\",\n borderRadius: \"6px\",\n border: \"1px solid var(--color-border-default, #ccc)\",\n padding: \"6px 8px\",\n fontSize: \"12px\",\n fontFamily: \"inherit\",\n resize: \"vertical\",\n boxSizing: \"border-box\",\n marginTop: \"8px\",\n } as React.CSSProperties,\n\n muted: {\n color: \"var(--color-fg-muted, #888)\",\n fontSize: \"12px\",\n } as React.CSSProperties,\n\n mono: {\n fontFamily: \"monospace\",\n fontSize: \"11px\",\n background: \"var(--color-bg-subtle, #f0f0f0)\",\n padding: \"1px 5px\",\n borderRadius: \"3px\",\n } as React.CSSProperties,\n} as const;\n\n// \u2500\u2500 Status badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst STATUS_COLORS: Record<string, string> = {\n open: \"#2563eb\",\n in_progress: \"#d97706\",\n in_review: \"#7c3aed\",\n completed: \"#16a34a\",\n cancelled: \"#6b7280\",\n rejected: \"#dc2626\",\n submitted: \"#7c3aed\",\n accepted: \"#d97706\",\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n const color = STATUS_COLORS[status] ?? \"#6b7280\";\n return <span style={styles.badge(color)}>{status.replace(/_/g, \" \")}</span>;\n}\n\n// \u2500\u2500 Participation item \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ParticipationItem({\n p,\n}: {\n p: AcnTaskInfo[\"participations\"][number];\n}) {\n const [expanded, setExpanded] = useState(false);\n const hasSubmission = Boolean(p.submission_content);\n\n return (\n <div style={styles.participationCard}>\n <div style={styles.row}>\n <span style={styles.label}>Agent</span>\n <span style={styles.mono}>{p.agent_id}</span>\n </div>\n <div style={styles.row}>\n <span style={styles.label}>Status</span>\n <StatusBadge status={p.status} />\n {p.resubmit_count > 0 && (\n <span style={styles.muted}>({p.resubmit_count} resubmit{p.resubmit_count > 1 ? \"s\" : \"\"})</span>\n )}\n </div>\n {p.submitted_at && (\n <div style={styles.row}>\n <span style={styles.label}>Submitted</span>\n <span style={styles.muted}>{new Date(p.submitted_at).toLocaleString()}</span>\n </div>\n )}\n {hasSubmission && (\n <>\n <button\n type=\"button\"\n style={{ ...styles.btn(\"neutral\"), marginTop: \"6px\", fontSize: \"11px\", padding: \"3px 10px\" }}\n onClick={() => setExpanded((v) => !v)}\n >\n {expanded ? \"Hide submission\" : \"Show submission\"}\n </button>\n {expanded && <pre style={styles.pre}>{p.submission_content}</pre>}\n </>\n )}\n </div>\n );\n}\n\n// \u2500\u2500 Review panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction ReviewPanel({\n taskId,\n onDone,\n}: {\n taskId: string;\n onDone: () => void;\n}) {\n const [feedback, setFeedback] = useState(\"\");\n const [pending, setPending] = useState<\"approve\" | \"reject\" | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const review = usePluginAction(\"acn-review\");\n\n async function handleReview(approved: boolean) {\n const action = approved ? \"approve\" : \"reject\";\n setPending(action);\n setError(null);\n try {\n await review({ taskId, approved, feedback: feedback.trim() || undefined });\n onDone();\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Review failed\");\n } finally {\n setPending(null);\n }\n }\n\n return (\n <div style={styles.section}>\n <div style={styles.sectionTitle}>Review Submission</div>\n <textarea\n style={styles.textarea}\n placeholder=\"Optional feedback\u2026\"\n value={feedback}\n onChange={(e) => setFeedback(e.target.value)}\n disabled={pending !== null}\n />\n {error && <div style={{ color: \"#dc2626\", fontSize: \"12px\", marginTop: \"4px\" }}>{error}</div>}\n <div style={styles.actionRow}>\n <button\n type=\"button\"\n style={{ ...styles.btn(\"approve\"), opacity: pending ? 0.6 : 1 }}\n disabled={pending !== null}\n onClick={() => void handleReview(true)}\n >\n {pending === \"approve\" ? \"Approving\u2026\" : \"Approve\"}\n </button>\n <button\n type=\"button\"\n style={{ ...styles.btn(\"reject\"), opacity: pending ? 0.6 : 1 }}\n disabled={pending !== null}\n onClick={() => void handleReview(false)}\n >\n {pending === \"reject\" ? \"Rejecting\u2026\" : \"Reject\"}\n </button>\n </div>\n </div>\n );\n}\n\n// \u2500\u2500 Main ACN Issue Tab \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport function ACNIssueTab({ context }: PluginDetailTabProps) {\n const issueId = context.entityId;\n const companyId = context.companyId;\n const [reviewDone, setReviewDone] = useState(false);\n\n const { data, loading, error } = usePluginData<AcnTaskInfo | null>(\n \"acn-task-info\",\n issueId && companyId ? { issueId, companyId } : {},\n );\n\n if (!issueId) {\n return <div style={styles.container}><span style={styles.muted}>No issue selected.</span></div>;\n }\n\n if (loading) {\n return <div style={styles.container}><span style={styles.muted}>Loading\u2026</span></div>;\n }\n\n if (error) {\n return (\n <div style={styles.container}>\n <span style={{ color: \"#dc2626\" }}>Failed to load ACN data: {error.message}</span>\n </div>\n );\n }\n\n if (!data) {\n return (\n <div style={styles.container}>\n <span style={styles.muted}>This issue is not linked to an ACN task.</span>\n </div>\n );\n }\n\n const submittedParticipation = data.participations.find(\n (p) => p.status === \"submitted\",\n );\n const canReview = !reviewDone && Boolean(submittedParticipation);\n\n return (\n <div style={styles.container}>\n {/* Task meta */}\n <div style={styles.row}>\n <span style={styles.label}>ACN Task</span>\n <span style={styles.mono}>{data.task_id}</span>\n </div>\n <div style={styles.row}>\n <span style={styles.label}>Status</span>\n <StatusBadge status={data.status} />\n </div>\n {parseFloat(data.reward ?? \"0\") > 0 && (\n <div style={styles.row}>\n <span style={styles.label}>Reward</span>\n <span>{data.reward} {data.reward_currency}</span>\n </div>\n )}\n\n {/* Participations */}\n {data.participations.length > 0 && (\n <div style={styles.section}>\n <div style={styles.sectionTitle}>\n Participants ({data.participations.length})\n </div>\n {data.participations.map((p) => (\n <ParticipationItem key={p.participation_id} p={p} />\n ))}\n </div>\n )}\n\n {/* Review panel \u2014 shown when there is a pending submission */}\n {canReview && (\n <ReviewPanel\n taskId={data.task_id}\n onDone={() => {\n setReviewDone(true);\n setTimeout(() => setReviewDone(false), 3000);\n }}\n />\n )}\n\n {reviewDone && (\n <div style={{ ...styles.section, color: \"#16a34a\", fontWeight: 600 }}>\n Review submitted. ACN will process the result.\n </div>\n )}\n </div>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ACN Plugin Worker
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* P0-1 Startup : register webhook URL as ACN subnet harness
|
|
6
|
+
* P0-2 Startup : full sync — pull open ACN tasks → Paperclip issues
|
|
7
|
+
* P0-3 ACN→PC : task.* webhook events → sync Paperclip issue status / comments
|
|
8
|
+
* P0-4 PC→ACN : Paperclip issue done/cancelled → ACN review (approve / reject)
|
|
9
|
+
* P0-5 PC→ACN : Paperclip issue created (not from ACN) → ACN task
|
|
10
|
+
*/
|
|
11
|
+
import type { PluginContext, PluginEvent } from "@paperclipai/plugin-sdk";
|
|
12
|
+
import { ACNClient } from "acn-client";
|
|
13
|
+
interface PluginConfig {
|
|
14
|
+
acnBaseUrl?: string;
|
|
15
|
+
paperclipBaseUrl?: string;
|
|
16
|
+
acnApiKeyRef?: string;
|
|
17
|
+
/** Secret ref for the HMAC-SHA256 secret shared with ACN's harness webhook. */
|
|
18
|
+
acnHarnessSecretRef?: string;
|
|
19
|
+
acnSubnetId?: string;
|
|
20
|
+
autoCreateIssues?: boolean;
|
|
21
|
+
autoApproveOnDone?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function handleIssueUpdated(ctx: PluginContext, cfg: PluginConfig, client: ACNClient, event: PluginEvent): Promise<void>;
|
|
24
|
+
export declare function handleIssueCreated(ctx: PluginContext, cfg: PluginConfig, client: ACNClient, event: PluginEvent): Promise<void>;
|
|
25
|
+
declare const plugin: import("@paperclipai/plugin-sdk").PaperclipPlugin;
|
|
26
|
+
export default plugin;
|
|
27
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE1E,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAQlD,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AA8SD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,YAAY,EACjB,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAwDf;AAID,QAAA,MAAM,MAAM,mDAuJV,CAAC;AAIH,eAAe,MAAM,CAAC"}
|