@preventive/triage 1.0.0-alpha.49 → 1.0.0-alpha.50
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/common/managed/finding-detail.d.ts +2 -0
- package/common/managed/finding-detail.js +32 -0
- package/common/managed/github-agents.ts +6 -3
- package/common/managed/routes.js +14 -2
- package/out/activity.js +4 -4
- package/out/{chunk-ULIF7A7N.js → chunk-2WDZPXVL.js} +1 -1
- package/out/chunk-4XMP7YVH.js +36 -0
- package/out/chunk-6S2U2UYG.js +232 -0
- package/out/{chunk-IO7JTZ6O.js → chunk-75YLHBCM.js} +1 -1
- package/out/chunk-FC65TPUB.js +1 -0
- package/out/{chunk-O6M574JI.js → chunk-FKBXNUW4.js} +1 -1
- package/out/{chunk-PBSOL2SL.js → chunk-HTBYHSPG.js} +1 -1
- package/out/{chunk-SZD5CW5R.js → chunk-UK7RGJT6.js} +2 -2
- package/out/client-managed-import.js +1 -1
- package/out/client-managed.js +2 -2
- package/out/compare.js +14 -14
- package/out/dialogs.js +34 -30
- package/out/graph.js +1 -1
- package/out/index.html +6 -6
- package/out/scan.js +2 -2
- package/out/view.css +1 -1
- package/out/view.js +15 -15
- package/package.json +4 -1
- package/server-managed/agent-merges.ts +31 -136
- package/server-managed/app-agents.ts +4 -0
- package/server-managed/commit-coauthors.ts +46 -24
- package/server-managed/http.ts +115 -37
- package/server-managed/npm-kept.ts +8 -0
- package/server-managed/public-workspace.ts +2 -2
- package/server-managed/repository-agents.ts +189 -57
- package/server-managed/repository-history.ts +51 -13
- package/server-managed/repository-pulls.ts +156 -0
- package/server-managed/repository-pulse.ts +3 -2
- package/server-managed/repository-squashes.ts +29 -6
- package/server-managed/team-reports.ts +33 -18
- package/out/chunk-BC2UCLFJ.js +0 -1
- package/out/chunk-C2AVODGZ.js +0 -36
- package/out/chunk-JEAUAJVF.js +0 -232
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { findingTitle } from '@preventive/report'
|
|
2
|
+
|
|
3
|
+
// A finding's detail: what its popup, the views besides the board and an
|
|
4
|
+
// export read, and the board does not. Its narrative and evidence, the hash
|
|
5
|
+
// its Code button finds a bundle by, and the export it names. A team's
|
|
6
|
+
// reports are first sent without it (server-managed/team-reports.ts), the
|
|
7
|
+
// board drawn from them, and their findings filled in from the whole reports
|
|
8
|
+
// (ui/view/managed-details.js).
|
|
9
|
+
export const FINDING_DETAIL_FIELDS = Object.freeze(['description', 'evidence', 'impact', 'reproduction', 'recommendation',
|
|
10
|
+
'confidenceReason', 'revalidateVerdict', 'revalidateRecommendation', 'fileHash', 'exportName', 'methodName'])
|
|
11
|
+
const DETAIL = new Set(FINDING_DETAIL_FIELDS)
|
|
12
|
+
// More of a name than a board card's three lines show.
|
|
13
|
+
const NAME_CHARS = 200
|
|
14
|
+
|
|
15
|
+
// A finding without its detail. One without a title of its own is named by its
|
|
16
|
+
// description's first line (findingTitle), which it keeps as its description,
|
|
17
|
+
// as much of it as a card shows.
|
|
18
|
+
function liteFinding(finding) {
|
|
19
|
+
if (!finding || typeof finding !== 'object' || Array.isArray(finding)) return finding
|
|
20
|
+
const lite = Object.fromEntries(Object.entries(finding).filter(([key]) => !DETAIL.has(key)))
|
|
21
|
+
const titled = typeof finding.title === 'string' && finding.title.trim() !== ''
|
|
22
|
+
const title = titled ? '' : findingTitle(finding)
|
|
23
|
+
if (title) lite.description = title.length > NAME_CHARS ? `${title.slice(0, NAME_CHARS - 1)}…` : title
|
|
24
|
+
return lite
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// A report's data, its findings without their detail.
|
|
28
|
+
export function liteReportData(data) {
|
|
29
|
+
const key = Array.isArray(data?.findings) ? 'findings' : Array.isArray(data?.groups) ? 'groups' : null
|
|
30
|
+
if (!key) return data
|
|
31
|
+
return { ...data, [key]: data[key].map(entry => Array.isArray(entry) ? entry.map(liteFinding) : liteFinding(entry)) }
|
|
32
|
+
}
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
// in its address. GitHub's Agentic Workflows (gh-aw) post, and approve or
|
|
21
21
|
// review pull requests, as GitHub Actions' bot, `github-actions` (41898282,
|
|
22
22
|
// `[bot]` to the REST API), its mark the GitHub Actions App's avatar; other
|
|
23
|
-
// workflows the bot runs are as much its own
|
|
23
|
+
// workflows the bot runs are as much its own, and the commits workflows make
|
|
24
|
+
// as the names they are commonly given, `github-actions` and `GitHub Action`,
|
|
25
|
+
// GitHub naming no account for their addresses. Neo, its GitHub App (3115942),
|
|
24
26
|
// as its account (274065190) and its App's bot (268952163), each known by
|
|
25
27
|
// its id alone, whatever GitHub names it, and drawn by its App's avatar,
|
|
26
28
|
// wherever either or its fingerprint names it. Cursor's agents commit as whoever ran them, with a
|
|
@@ -51,7 +53,8 @@ export const GITHUB_AGENTS = {
|
|
|
51
53
|
{ id: 175728472, logins: ['Copilot', 'copilot-pull-request-reviewer', 'copilot-pull-request-reviewer[bot]'] }], names: ['Copilot'], prefix: 'copilot/' },
|
|
52
54
|
dependabot: { name: 'Dependabot', avatar: 'app-29110', accounts: [{ id: 49699333, logins: ['dependabot', 'dependabot[bot]'] }], names: ['dependabot[bot]'],
|
|
53
55
|
prefix: 'dependabot/' },
|
|
54
|
-
actions: { name: 'GitHub Actions', avatar: 'app-15368', accounts: [{ id: 41898282, logins: ['github-actions', 'github-actions[bot]'] }],
|
|
56
|
+
actions: { name: 'GitHub Actions', avatar: 'app-15368', accounts: [{ id: 41898282, logins: ['github-actions', 'github-actions[bot]'] }],
|
|
57
|
+
names: ['github-actions[bot]', 'github-actions', 'GitHub Action'] },
|
|
55
58
|
cursor: { name: 'Cursor', accounts: [], names: ['Cursor Agent'], emails: ['cursoragent@cursor.com'] },
|
|
56
59
|
} as const satisfies Record<string, GithubAgent>
|
|
57
60
|
export type GithubAgentId = keyof typeof GITHUB_AGENTS
|
|
@@ -99,7 +102,7 @@ export function oneAgent(login: string, ids: Iterable<number>): boolean {
|
|
|
99
102
|
// The accounts agents work as, a person's to GitHub, as the app is told of
|
|
100
103
|
// them, each by its id and logins: the pull requests they open and the
|
|
101
104
|
// commits they push to a branch directly are agents' (the Pulse's,
|
|
102
|
-
// server-managed/
|
|
105
|
+
// server-managed/repository-pulls.ts readPulls, repository-pulse.ts
|
|
103
106
|
// commitStats), whatever their branches and labels, the rest of what they do
|
|
104
107
|
// theirs. Christopher Turing's.
|
|
105
108
|
export const AGENT_OPERATED = [{ id: 188797125, logins: ['ChristopherTuring'] }] as const satisfies readonly GithubAgentAccount[]
|
package/common/managed/routes.js
CHANGED
|
@@ -146,7 +146,8 @@ function parseNpmRoute(path, hash) {
|
|
|
146
146
|
// link opens, names its file by number and its lines in the fragment, as a
|
|
147
147
|
// bundle's Code does: `/code/<file>#L4`. Compare names the commit compared
|
|
148
148
|
// with, then its mode past the Overview, as an npm version's does:
|
|
149
|
-
// `/compare/<commit>/code`.
|
|
149
|
+
// `/compare/<commit>/code`. An Activity view names its sub-tab past the
|
|
150
|
+
// first (isActivitySubtab): `/pulse/year`. Without a commit,
|
|
150
151
|
// `/repo/<repo id>[?path=<directory>]`, it opens on the default
|
|
151
152
|
// branch's head, or on the revision a route held in memory names (`ref`),
|
|
152
153
|
// and its route then names the commit it opened. Links from before it moved
|
|
@@ -155,6 +156,15 @@ export const REPOSITORY_CODE_TABS = new Set(['code', 'search', 'treemap', 'compa
|
|
|
155
156
|
// The tabs reading none of the directory's files, which a directory at a
|
|
156
157
|
// commit shows alone.
|
|
157
158
|
export const REPOSITORY_FILELESS_TABS = new Set(['commits', 'issues', 'pulls', 'agents', 'pulse', 'replay'])
|
|
159
|
+
// The Activity views' sub-tabs past their first: Issues' and Pull requests'
|
|
160
|
+
// closed ones (open, the first), Pulse's periods (a month, the first),
|
|
161
|
+
// Replay's commits played (the last 300, the first), and one agent's
|
|
162
|
+
// sessions, by its id (every agent's, `all`, the first).
|
|
163
|
+
const ACTIVITY_SUBTABS = new Map([['issues', new Set(['closed'])], ['pulls', new Set(['closed'])],
|
|
164
|
+
['pulse', new Set(['week', 'quarter', 'year'])], ['replay', new Set(['100', '1000'])]])
|
|
165
|
+
const AGENT_ID = /^[a-z][a-z\d-]{0,31}$/u
|
|
166
|
+
export const isActivitySubtab = (tab, subtab) => tab === 'agents' ? typeof subtab === 'string' && AGENT_ID.test(subtab) && subtab !== 'all'
|
|
167
|
+
: ACTIVITY_SUBTABS.get(tab)?.has(subtab) === true
|
|
158
168
|
const COMMIT = /^[a-f\d]{40}$/u
|
|
159
169
|
const MAX_DIRECTORY = 500
|
|
160
170
|
const isRepoId = value => Number.isSafeInteger(value) && value > 0
|
|
@@ -176,9 +186,10 @@ function repositoryCodePath(route) {
|
|
|
176
186
|
if (route.commit == null) return `/repo/${route.repoId}${directory}`
|
|
177
187
|
const file = tab === 'code' && isLineNumber(route.file) ? `/code/${route.file}` : tab === 'code' ? '' : `/${tab}`
|
|
178
188
|
const compare = tab === 'compare' && route.compareCommit != null ? `/${route.compareCommit}${compareModeSuffix(route.compareMode)}` : ''
|
|
189
|
+
const subtab = isActivitySubtab(tab, route.subtab) ? `/${route.subtab}` : ''
|
|
179
190
|
const lines = tab === 'code' && isLineNumber(route.file) && isLineNumber(route.line)
|
|
180
191
|
? `#L${route.line}${isLineNumber(route.endLine) && route.endLine > route.line ? `-L${route.endLine}` : ''}` : ''
|
|
181
|
-
return `/repo/${route.repoId}/${route.commit}${file}${compare}${directory}${lines}`
|
|
192
|
+
return `/repo/${route.repoId}/${route.commit}${file}${compare}${subtab}${directory}${lines}`
|
|
182
193
|
}
|
|
183
194
|
|
|
184
195
|
function parseRepositoryCodeRoute(url) {
|
|
@@ -195,6 +206,7 @@ function parseRepositoryCodeRoute(url) {
|
|
|
195
206
|
if (tab === 'compare') {
|
|
196
207
|
return COMMIT.test(match[4]) && (match[5] == null || COMPARE_MODES.has(match[5])) ? { ...route, compareCommit: match[4], ...compareModeField(match[5]) } : null
|
|
197
208
|
}
|
|
209
|
+
if (REPOSITORY_FILELESS_TABS.has(tab)) return match[5] == null && isActivitySubtab(tab, match[4]) ? { ...route, subtab: match[4] } : null
|
|
198
210
|
const file = Number(match[4])
|
|
199
211
|
return tab === 'code' && match[5] == null && /^[1-9]\d*$/u.test(match[4]) && Number.isSafeInteger(file) ? { ...route, file, ...codeLines(url.hash) } : null
|
|
200
212
|
}
|