@a5c-ai/babysitter-paperclip 0.0.2-staging.02a0ee21
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/BABYSITTER.md +46 -0
- package/README.md +229 -0
- package/esbuild.config.mjs +12 -0
- package/package.json +29 -0
- package/src/__tests__/delegating-adapter.test.ts +85 -0
- package/src/__tests__/types.test.ts +29 -0
- package/src/babysitter-bridge.ts +313 -0
- package/src/delegating-adapter.ts +97 -0
- package/src/harness-plugin-installer.ts +202 -0
- package/src/manifest.ts +101 -0
- package/src/types.ts +130 -0
- package/src/ui/BabysitterDashboard.tsx +142 -0
- package/src/ui/BabysitterSidebar.tsx +123 -0
- package/src/ui/BreakpointApproval.tsx +212 -0
- package/src/ui/RunDetailTab.tsx +169 -0
- package/src/ui/index.tsx +9 -0
- package/src/ui/styles.ts +75 -0
- package/src/worker.ts +595 -0
- package/tsconfig.json +19 -0
- package/versions.json +3 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run detail tab showing full journal timeline and breakpoint approval.
|
|
3
|
+
*
|
|
4
|
+
* Rendered as a detail tab on agent entities in the Paperclip UI.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { usePluginData, usePluginStream } from "@paperclipai/plugin-sdk/ui";
|
|
8
|
+
import type { PluginDetailTabProps } from "@paperclipai/plugin-sdk/ui";
|
|
9
|
+
import { BreakpointApproval } from "./BreakpointApproval";
|
|
10
|
+
|
|
11
|
+
interface RunEvent {
|
|
12
|
+
seq: number;
|
|
13
|
+
type: string;
|
|
14
|
+
recordedAt: string;
|
|
15
|
+
data: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PendingEffect {
|
|
19
|
+
effectId: string;
|
|
20
|
+
kind: string;
|
|
21
|
+
label: string;
|
|
22
|
+
taskId: string;
|
|
23
|
+
requestedAt: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface RunDetail {
|
|
27
|
+
run: {
|
|
28
|
+
runId: string;
|
|
29
|
+
processId: string;
|
|
30
|
+
status: string;
|
|
31
|
+
pendingBreakpoints: Array<{
|
|
32
|
+
effectId: string;
|
|
33
|
+
title: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
options?: string[];
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
events: RunEvent[];
|
|
39
|
+
pendingEffects: PendingEffect[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface StreamEvent {
|
|
43
|
+
type: string;
|
|
44
|
+
data: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function RunDetailTab({ context }: PluginDetailTabProps) {
|
|
48
|
+
const runId = context.entityId;
|
|
49
|
+
const { data, loading, error, refresh } = usePluginData<RunDetail>(
|
|
50
|
+
"run-detail",
|
|
51
|
+
{ runId, companyId: context.companyId }
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const { events: streamEvents } = usePluginStream<StreamEvent>(
|
|
55
|
+
`run-events:${runId}`,
|
|
56
|
+
{ companyId: context.companyId ?? undefined }
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (loading) return <div style={{ padding: 16 }}>Loading run detail...</div>;
|
|
60
|
+
if (error) {
|
|
61
|
+
return (
|
|
62
|
+
<div style={{ padding: 16, color: "var(--destructive)" }}>
|
|
63
|
+
Error: {error.message}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (!data) return null;
|
|
68
|
+
|
|
69
|
+
const { run, events, pendingEffects } = data;
|
|
70
|
+
const breakpoints = pendingEffects.filter((e) => e.kind === "breakpoint");
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div style={{ display: "grid", gap: 16, padding: 16 }}>
|
|
74
|
+
{/* Header */}
|
|
75
|
+
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
76
|
+
<div>
|
|
77
|
+
<strong style={{ fontSize: 16 }}>{run.processId}</strong>
|
|
78
|
+
<div style={{ fontSize: 12, color: "var(--muted-foreground)", fontFamily: "monospace" }}>
|
|
79
|
+
{run.runId}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
|
|
83
|
+
<span
|
|
84
|
+
style={{
|
|
85
|
+
padding: "2px 8px",
|
|
86
|
+
borderRadius: 4,
|
|
87
|
+
fontSize: 12,
|
|
88
|
+
background:
|
|
89
|
+
run.status === "waiting"
|
|
90
|
+
? "#eab308"
|
|
91
|
+
: run.status === "running"
|
|
92
|
+
? "#22c55e"
|
|
93
|
+
: run.status === "failed"
|
|
94
|
+
? "#ef4444"
|
|
95
|
+
: "#6b7280",
|
|
96
|
+
color: "white",
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
{run.status}
|
|
100
|
+
</span>
|
|
101
|
+
<button onClick={refresh} style={{ fontSize: 12 }}>
|
|
102
|
+
Refresh
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
{/* Pending breakpoints */}
|
|
108
|
+
{breakpoints.length > 0 && (
|
|
109
|
+
<div style={{ display: "grid", gap: 8 }}>
|
|
110
|
+
<strong style={{ fontSize: 14 }}>
|
|
111
|
+
Pending Breakpoints ({breakpoints.length})
|
|
112
|
+
</strong>
|
|
113
|
+
{breakpoints.map((bp) => (
|
|
114
|
+
<BreakpointApproval
|
|
115
|
+
key={bp.effectId}
|
|
116
|
+
runId={run.runId}
|
|
117
|
+
effectId={bp.effectId}
|
|
118
|
+
title={bp.label}
|
|
119
|
+
companyId={context.companyId ?? ""}
|
|
120
|
+
onResolved={refresh}
|
|
121
|
+
/>
|
|
122
|
+
))}
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
{/* Journal timeline */}
|
|
127
|
+
<div>
|
|
128
|
+
<strong style={{ fontSize: 14 }}>Journal Timeline</strong>
|
|
129
|
+
<div style={{ marginTop: 8, display: "grid", gap: 4 }}>
|
|
130
|
+
{events.map((evt) => (
|
|
131
|
+
<div
|
|
132
|
+
key={evt.seq}
|
|
133
|
+
style={{
|
|
134
|
+
display: "grid",
|
|
135
|
+
gridTemplateColumns: "40px 140px 1fr",
|
|
136
|
+
gap: 8,
|
|
137
|
+
padding: "4px 8px",
|
|
138
|
+
borderRadius: 4,
|
|
139
|
+
background: "var(--muted)",
|
|
140
|
+
fontSize: 12,
|
|
141
|
+
fontFamily: "monospace",
|
|
142
|
+
}}
|
|
143
|
+
>
|
|
144
|
+
<span style={{ color: "var(--muted-foreground)" }}>#{evt.seq}</span>
|
|
145
|
+
<span>{evt.type}</span>
|
|
146
|
+
<span style={{ color: "var(--muted-foreground)" }}>
|
|
147
|
+
{new Date(evt.recordedAt).toLocaleTimeString()}
|
|
148
|
+
</span>
|
|
149
|
+
</div>
|
|
150
|
+
))}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
{/* Live stream events */}
|
|
155
|
+
{streamEvents.length > 0 && (
|
|
156
|
+
<div>
|
|
157
|
+
<strong style={{ fontSize: 14 }}>Live Events</strong>
|
|
158
|
+
<div style={{ marginTop: 8, maxHeight: 200, overflow: "auto" }}>
|
|
159
|
+
{streamEvents.map((evt, i) => (
|
|
160
|
+
<div key={i} style={{ fontSize: 11, fontFamily: "monospace", padding: 2 }}>
|
|
161
|
+
[{evt.type}] {JSON.stringify(evt.data).slice(0, 120)}
|
|
162
|
+
</div>
|
|
163
|
+
))}
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
)}
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
package/src/ui/index.tsx
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI entry point for the Babysitter Paperclip plugin.
|
|
3
|
+
*
|
|
4
|
+
* Exports all slot components referenced in the manifest.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { BabysitterDashboard } from "./BabysitterDashboard";
|
|
8
|
+
export { RunDetailTab } from "./RunDetailTab";
|
|
9
|
+
export { BabysitterSidebar } from "./BabysitterSidebar";
|
package/src/ui/styles.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared inline style objects for Babysitter UI components.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CSSProperties } from "react";
|
|
6
|
+
|
|
7
|
+
export const card: CSSProperties = {
|
|
8
|
+
border: "1px solid var(--border)",
|
|
9
|
+
borderRadius: 8,
|
|
10
|
+
padding: 16,
|
|
11
|
+
display: "grid",
|
|
12
|
+
gap: 12,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const badge = (color: string): CSSProperties => ({
|
|
16
|
+
display: "inline-flex",
|
|
17
|
+
alignItems: "center",
|
|
18
|
+
gap: 4,
|
|
19
|
+
padding: "2px 8px",
|
|
20
|
+
borderRadius: 4,
|
|
21
|
+
fontSize: 12,
|
|
22
|
+
background: color,
|
|
23
|
+
color: "white",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const statusColors: Record<string, string> = {
|
|
27
|
+
running: "#22c55e",
|
|
28
|
+
waiting: "#eab308",
|
|
29
|
+
completed: "#6b7280",
|
|
30
|
+
failed: "#ef4444",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const mono: CSSProperties = {
|
|
34
|
+
fontFamily: "monospace",
|
|
35
|
+
fontSize: 12,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const muted: CSSProperties = {
|
|
39
|
+
color: "var(--muted-foreground)",
|
|
40
|
+
fontSize: 13,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const row: CSSProperties = {
|
|
44
|
+
display: "flex",
|
|
45
|
+
justifyContent: "space-between",
|
|
46
|
+
alignItems: "center",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const grid = (gap = 8): CSSProperties => ({
|
|
50
|
+
display: "grid",
|
|
51
|
+
gap,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const button: CSSProperties = {
|
|
55
|
+
fontSize: 12,
|
|
56
|
+
padding: "4px 12px",
|
|
57
|
+
borderRadius: 4,
|
|
58
|
+
border: "1px solid var(--border)",
|
|
59
|
+
cursor: "pointer",
|
|
60
|
+
background: "transparent",
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const primaryButton: CSSProperties = {
|
|
64
|
+
...button,
|
|
65
|
+
background: "var(--primary)",
|
|
66
|
+
color: "var(--primary-foreground)",
|
|
67
|
+
border: "none",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const destructiveButton: CSSProperties = {
|
|
71
|
+
...button,
|
|
72
|
+
background: "var(--destructive)",
|
|
73
|
+
color: "var(--destructive-foreground)",
|
|
74
|
+
border: "none",
|
|
75
|
+
};
|