openhacker 0.1.2 → 0.1.3
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
|
@@ -4,7 +4,6 @@ You are OpenHacker, an application security agent.
|
|
|
4
4
|
|
|
5
5
|
The user gives you a GitHub repository (as `owner/name` or a github.com URL). Analyze it for security vulnerabilities and report your findings.
|
|
6
6
|
|
|
7
|
-
- Briefly narrate what you are checking as you go (dependencies, auth, input handling, secrets, etc.).
|
|
8
7
|
- Use the shell to clone and explore the repo. Use `ls`/`find` to list directories; only use `read_file` on actual file paths, never on a directory.
|
|
9
8
|
- Call out concrete risks with severity and, where possible, how to fix them.
|
|
10
9
|
- If you cannot access the repository, say so plainly.
|
|
@@ -95,13 +95,35 @@ h1 span {
|
|
|
95
95
|
margin-bottom: 0;
|
|
96
96
|
}
|
|
97
97
|
.reply .reasoning {
|
|
98
|
-
white-space: pre-wrap;
|
|
99
|
-
color: var(--muted);
|
|
100
|
-
font-size: 13px;
|
|
101
98
|
margin: 0 0 12px;
|
|
102
99
|
padding-left: 12px;
|
|
103
100
|
border-left: 2px solid var(--border);
|
|
104
101
|
}
|
|
102
|
+
.reply .reasoning > summary {
|
|
103
|
+
cursor: pointer;
|
|
104
|
+
color: var(--muted);
|
|
105
|
+
font-size: 12px;
|
|
106
|
+
text-transform: uppercase;
|
|
107
|
+
letter-spacing: 0.5px;
|
|
108
|
+
list-style: none;
|
|
109
|
+
user-select: none;
|
|
110
|
+
}
|
|
111
|
+
.reply .reasoning > summary::-webkit-details-marker {
|
|
112
|
+
display: none;
|
|
113
|
+
}
|
|
114
|
+
.reply .reasoning > summary::before {
|
|
115
|
+
content: "› ";
|
|
116
|
+
display: inline-block;
|
|
117
|
+
}
|
|
118
|
+
.reply .reasoning[open] > summary::before {
|
|
119
|
+
content: "⌄ ";
|
|
120
|
+
}
|
|
121
|
+
.reply .reasoning > p {
|
|
122
|
+
white-space: pre-wrap;
|
|
123
|
+
color: var(--muted);
|
|
124
|
+
font-size: 13px;
|
|
125
|
+
margin: 8px 0 0;
|
|
126
|
+
}
|
|
105
127
|
|
|
106
128
|
.tool {
|
|
107
129
|
display: flex;
|
|
@@ -123,6 +145,33 @@ h1 span {
|
|
|
123
145
|
padding: 0 8px;
|
|
124
146
|
}
|
|
125
147
|
|
|
148
|
+
.hacking {
|
|
149
|
+
color: var(--muted);
|
|
150
|
+
font-size: 13px;
|
|
151
|
+
margin: 0;
|
|
152
|
+
}
|
|
153
|
+
.reply .text + .hacking {
|
|
154
|
+
margin-top: 12px;
|
|
155
|
+
}
|
|
156
|
+
.dots::after {
|
|
157
|
+
content: "";
|
|
158
|
+
animation: dots 1.2s steps(4, end) infinite;
|
|
159
|
+
}
|
|
160
|
+
@keyframes dots {
|
|
161
|
+
0% {
|
|
162
|
+
content: "";
|
|
163
|
+
}
|
|
164
|
+
25% {
|
|
165
|
+
content: ".";
|
|
166
|
+
}
|
|
167
|
+
50% {
|
|
168
|
+
content: "..";
|
|
169
|
+
}
|
|
170
|
+
75% {
|
|
171
|
+
content: "...";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
126
175
|
.cursor {
|
|
127
176
|
display: inline-block;
|
|
128
177
|
width: 8px;
|
|
@@ -15,7 +15,7 @@ export default function Home() {
|
|
|
15
15
|
if (!value || busy) return;
|
|
16
16
|
agent.reset();
|
|
17
17
|
agent.send({
|
|
18
|
-
message: `Analyze the GitHub repository "${value}" for security vulnerabilities.
|
|
18
|
+
message: `Analyze the GitHub repository "${value}" for security vulnerabilities. Reply with only the final report.`,
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -23,6 +23,20 @@ export default function Home() {
|
|
|
23
23
|
.reverse()
|
|
24
24
|
.find((m) => m.role === "assistant");
|
|
25
25
|
|
|
26
|
+
const parts = reply?.parts ?? [];
|
|
27
|
+
const lastStep = parts.reduce<number | undefined>((max, p) => {
|
|
28
|
+
const idx = "stepIndex" in p ? p.stepIndex : undefined;
|
|
29
|
+
if (typeof idx !== "number") return max;
|
|
30
|
+
return max === undefined ? idx : Math.max(max, idx);
|
|
31
|
+
}, undefined);
|
|
32
|
+
|
|
33
|
+
let result = "";
|
|
34
|
+
for (const p of parts) {
|
|
35
|
+
if (p.type !== "text") continue;
|
|
36
|
+
if (lastStep !== undefined && p.stepIndex !== lastStep) continue;
|
|
37
|
+
result += p.text;
|
|
38
|
+
}
|
|
39
|
+
|
|
26
40
|
return (
|
|
27
41
|
<main className="container">
|
|
28
42
|
<h1>
|
|
@@ -45,41 +59,16 @@ export default function Home() {
|
|
|
45
59
|
</button>
|
|
46
60
|
</form>
|
|
47
61
|
|
|
48
|
-
{reply ? (
|
|
62
|
+
{reply || busy ? (
|
|
49
63
|
<section className="reply">
|
|
50
|
-
{
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
if (part.type === "text") {
|
|
59
|
-
return (
|
|
60
|
-
<p key={i} className="text">
|
|
61
|
-
{part.text}
|
|
62
|
-
</p>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (part.type === "dynamic-tool") {
|
|
66
|
-
return (
|
|
67
|
-
<div key={i} className="tool">
|
|
68
|
-
<span className="tool-name">{part.toolName}</span>
|
|
69
|
-
<span className="tool-state">{part.state}</span>
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
}
|
|
73
|
-
return null;
|
|
74
|
-
})}
|
|
75
|
-
{agent.status === "streaming" ? (
|
|
76
|
-
<span className="cursor" aria-hidden />
|
|
64
|
+
{result ? <p className="text">{result}</p> : null}
|
|
65
|
+
{busy && !result ? (
|
|
66
|
+
<p className="hacking">
|
|
67
|
+
hacking
|
|
68
|
+
<span className="dots" aria-hidden />
|
|
69
|
+
</p>
|
|
77
70
|
) : null}
|
|
78
71
|
</section>
|
|
79
|
-
) : busy ? (
|
|
80
|
-
<section className="reply">
|
|
81
|
-
<span className="cursor" aria-hidden />
|
|
82
|
-
</section>
|
|
83
72
|
) : null}
|
|
84
73
|
|
|
85
74
|
{agent.status === "error" ? (
|