eve-hub 0.6.64 → 0.6.66
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/CHANGELOG.md +12 -0
- package/onboarding/files.gen.mjs +1 -1
- package/onboarding/github-facts.mjs +5 -5
- package/package.json +1 -1
- package/world.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# eve-hub
|
|
2
2
|
|
|
3
|
+
## 0.6.66
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- An upgrade of a workspace with no GitHub repo fails as a permanent `no-repo` kind: the card reads "Workspace cannot upgrade: No repository" with no Retry.
|
|
8
|
+
|
|
9
|
+
## 0.6.65
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- The onboarding-profile GitHub script runs on the `gh` in the sandbox (2.46): it no longer passes `--slurp`, which `gh` has only from 2.48.
|
|
14
|
+
|
|
3
15
|
## 0.6.64
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/onboarding/files.gen.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by scripts/build-onboarding.mjs. Do not edit.
|
|
2
|
-
export default {"github-facts.mjs":"#!/usr/bin/env node\n// Facts about the signed-in `gh` user's last 90 days on GitHub, as one JSON document on stdout.\n// Counts, titles (<=200 chars), URLs and CODEOWNERS lines only; no judgment: the model reads this and profiles.\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { pathToFileURL } from \"node:url\";\nimport { snippet } from \"./redact.mjs\";\n\nconst exec = promisify(execFile);\nconst DAYS = 90;\nconst LIST_CAP = 150;\nconst CODEOWNERS_REPOS = 10;\nconst STATEFUL = new Set([\"open\", \"reviewRequested\"]);\nconst CODEOWNERS_PATHS = [\".github/CODEOWNERS\", \"CODEOWNERS\", \"docs/CODEOWNERS\"];\n\nconst BASE = `\n __typename\n ... on PullRequest { number title url createdAt updatedAt mergedAt isDraft additions deletions changedFiles repository { nameWithOwner } author { login } @STATE@ }\n ... on Issue { number title url createdAt updatedAt repository { nameWithOwner } author { login } }`;\nconst STATE = \"reviewDecision mergeable reviewRequests { totalCount } commits(last: 1) { nodes { commit { statusCheckRollup { state } } } }\";\nconst searchQuery = (withState) => `query($q: String!, $endCursor: String) {\n search(query: $q, type: ISSUE, first: 50, after: $endCursor) {\n issueCount\n pageInfo { hasNextPage endCursor }\n nodes {${BASE.replace(\"@STATE@\", withState ? STATE : \"\")}}\n }\n}`;\n\nexport const realRun = async (args) => (await exec(\"gh\", args, { maxBuffer: 1 << 28 })).stdout;\n\nconst ATTEMPTS = 3;\n\nasync function search(run, q, withState = false) {\n const args = [\"api\", \"graphql\", \"--paginate\", \"--
|
|
2
|
+
export default {"github-facts.mjs":"#!/usr/bin/env node\n// Facts about the signed-in `gh` user's last 90 days on GitHub, as one JSON document on stdout.\n// Counts, titles (<=200 chars), URLs and CODEOWNERS lines only; no judgment: the model reads this and profiles.\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { pathToFileURL } from \"node:url\";\nimport { snippet } from \"./redact.mjs\";\n\nconst exec = promisify(execFile);\nconst DAYS = 90;\nconst LIST_CAP = 150;\nconst CODEOWNERS_REPOS = 10;\nconst STATEFUL = new Set([\"open\", \"reviewRequested\"]);\nconst CODEOWNERS_PATHS = [\".github/CODEOWNERS\", \"CODEOWNERS\", \"docs/CODEOWNERS\"];\n\nconst BASE = `\n __typename\n ... on PullRequest { number title url createdAt updatedAt mergedAt isDraft additions deletions changedFiles repository { nameWithOwner } author { login } @STATE@ }\n ... on Issue { number title url createdAt updatedAt repository { nameWithOwner } author { login } }`;\nconst STATE = \"reviewDecision mergeable reviewRequests { totalCount } commits(last: 1) { nodes { commit { statusCheckRollup { state } } } }\";\nconst searchQuery = (withState) => `query($q: String!, $endCursor: String) {\n search(query: $q, type: ISSUE, first: 50, after: $endCursor) {\n issueCount\n pageInfo { hasNextPage endCursor }\n nodes {${BASE.replace(\"@STATE@\", withState ? STATE : \"\")}}\n }\n}`;\n\nexport const realRun = async (args) => (await exec(\"gh\", args, { maxBuffer: 1 << 28 })).stdout;\n\nconst ATTEMPTS = 3;\n\nasync function search(run, q, withState = false) {\n const args = [\"api\", \"graphql\", \"--paginate\", \"--jq\", \".data.search\", \"-f\", `query=${searchQuery(withState)}`, \"-f\", `q=${q}`];\n let out;\n for (let attempt = 1; ; attempt++) {\n try {\n out = await run(args);\n break;\n } catch (e) {\n if (attempt === ATTEMPTS) throw e;\n await new Promise((r) => setTimeout(r, 1000 * attempt + Math.random() * 1000));\n }\n }\n const pages = out.split(\"\\n\").filter(Boolean).map((l) => JSON.parse(l));\n const nodes = pages.flatMap((p) => p.nodes).filter((n) => n?.url);\n return { total: pages[0].issueCount, nodes };\n}\n\nconst item = (n) => ({\n repo: n.repository.nameWithOwner,\n number: n.number,\n type: n.__typename,\n title: snippet(n.title),\n url: n.url,\n author: n.author?.login ?? null,\n createdAt: n.createdAt,\n updatedAt: n.updatedAt,\n ...(n.__typename === \"PullRequest\"\n ? {\n mergedAt: n.mergedAt,\n draft: n.isDraft,\n additions: n.additions,\n deletions: n.deletions,\n changedFiles: n.changedFiles,\n reviewDecision: n.reviewDecision ?? null,\n mergeable: n.mergeable ?? null,\n checks: n.commits?.nodes?.[0]?.commit?.statusCheckRollup?.state ?? null,\n reviewRequests: n.reviewRequests?.totalCount ?? null,\n }\n : {}),\n});\n\nconst byRecent = (a, b) => (b.mergedAt ?? b.updatedAt).localeCompare(a.mergedAt ?? a.updatedAt);\nconst listed = ({ total, nodes }) => ({ total, listed: Math.min(nodes.length, LIST_CAP), items: nodes.map(item).sort(byRecent).slice(0, LIST_CAP) });\n\nfunction perRepo(sets) {\n const repos = {};\n for (const [key, nodes] of Object.entries(sets)) {\n for (const n of nodes) {\n const r = (repos[n.repository.nameWithOwner] ??= { repo: n.repository.nameWithOwner, merged: 0, reviewed: 0, open: 0, additions: 0, deletions: 0 });\n r[key] += 1;\n if (key === \"merged\") { r.additions += n.additions ?? 0; r.deletions += n.deletions ?? 0; }\n }\n }\n return Object.values(repos).sort((a, b) => b.merged + b.reviewed + b.open - (a.merged + a.reviewed + a.open));\n}\n\nasync function codeownersLines(run, repo, login, teams) {\n const owners = [`@${login}`, ...teams.map((t) => `@${t}`)].map((o) => o.toLowerCase());\n for (const path of CODEOWNERS_PATHS) {\n let text;\n try {\n text = await run([\"api\", `repos/${repo}/contents/${path}`, \"-H\", \"Accept: application/vnd.github.raw+json\"]);\n } catch (e) {\n if (/HTTP 404/.test(e.stderr ?? e.message)) continue;\n throw e;\n }\n return text.split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l && !l.startsWith(\"#\") && l.toLowerCase().split(/\\s+/).some((tok) => owners.includes(tok)))\n .map((line) => ({ repo, path, line: snippet(line) }));\n }\n return [];\n}\n\nexport async function collect({ run = realRun, now = new Date() } = {}) {\n const since = new Date(now.getTime() - DAYS * 86400_000).toISOString().slice(0, 10);\n const unreadable = [];\n const attempt = async (what, fn, fallback) => {\n try {\n return await fn();\n } catch (e) {\n unreadable.push({ what, error: snippet(e.stderr || e.message) });\n return fallback;\n }\n };\n const empty = { total: 0, nodes: [] };\n const login = JSON.parse(await run([\"api\", \"user\"])).login;\n const teams = await attempt(\"user teams\", async () => (await run([\"api\", \"user/teams\", \"--paginate\", \"--jq\", '.[] | .organization.login + \"/\" + .slug'])).split(\"\\n\").filter(Boolean), []);\n const q = {\n merged: `is:pr author:@me is:merged merged:>=${since} archived:false`,\n open: \"is:pr author:@me is:open archived:false\",\n reviewRequested: \"is:pr is:open review-requested:@me archived:false\",\n assignedIssues: \"is:issue is:open assignee:@me archived:false\",\n unansweredMentions: `is:open mentions:@me -commenter:@me -author:@me updated:>=${since} archived:false`,\n reviewed: `is:pr reviewed-by:@me -author:@me merged:>=${since} archived:false`,\n };\n const got = Object.fromEntries(await Promise.all(Object.entries(q).map(async ([k, v]) => [k, await attempt(`search ${k}`, () => search(run, v, STATEFUL.has(k)), empty)])));\n const repos = perRepo({ merged: got.merged.nodes, reviewed: got.reviewed.nodes, open: got.open.nodes });\n const owned = (await Promise.all(repos.slice(0, CODEOWNERS_REPOS).map((r) => attempt(`CODEOWNERS ${r.repo}`, () => codeownersLines(run, r.repo, login, teams), [])))).flat();\n return {\n login,\n window: { since, until: now.toISOString().slice(0, 10), days: DAYS },\n counts: Object.fromEntries(Object.entries(got).map(([k, v]) => [k, v.total])),\n repos: repos.slice(0, 30),\n merged: listed(got.merged),\n open: listed(got.open),\n reviewRequested: listed(got.reviewRequested),\n assignedIssues: listed(got.assignedIssues),\n unansweredMentions: listed(got.unansweredMentions),\n teams,\n codeowners: owned,\n unreadable,\n };\n}\n\nif (import.meta.url === pathToFileURL(process.argv[1] ?? \"\").href) {\n process.stdout.write(JSON.stringify(await collect(), null, 1) + \"\\n\");\n}\n","redact.mjs":"const TOKEN = /\\b(?:xox[a-z]-[A-Za-z0-9-]{8,}|sk-[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{16,}|vck_[A-Za-z0-9]{16,}|AKIA[A-Z0-9]{16}|eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]*)/g;\n\nexport const MAX_TEXT = 200;\n\nexport function snippet(text) {\n const flat = String(text ?? \"\").replace(/\\s+/g, \" \").trim().replace(TOKEN, \"[redacted]\");\n return flat.length > MAX_TEXT ? flat.slice(0, MAX_TEXT - 1) + \"…\" : flat;\n}\n","check-report.mjs":"#!/usr/bin/env node\n// Checks the model-written profile against the facts it came from:\n// --profile p.md --facts a.json [b.json...] every URL in the profile is in the facts, and every list item cites one\n// Exits 1 and prints one line per problem.\nimport { readFileSync } from \"node:fs\";\nimport { pathToFileURL } from \"node:url\";\n\nconst URL_RE = /https?:\\/\\/[^\\s)>\\]\"'`]+/g;\nconst trimUrl = (u) => u.replace(/[.,;:!?]+$/, \"\");\n\nexport function factUrls(facts) {\n const urls = new Set();\n const walk = (v) => {\n if (typeof v === \"string\") { if (/^https?:\\/\\//.test(v)) urls.add(v); }\n else if (v && typeof v === \"object\") Object.values(v).forEach(walk);\n };\n facts.forEach(walk);\n return urls;\n}\n\nexport function citationProblems(profile, facts) {\n const known = factUrls(facts);\n const problems = [];\n profile.split(\"\\n\").forEach((line, i) => {\n const urls = (line.match(URL_RE) ?? []).map(trimUrl);\n for (const u of urls) if (!known.has(u)) problems.push(`line ${i + 1}: ${u} is not in the facts`);\n if (/^\\s*(?:[-*]|\\d+\\.)\\s/.test(line) && !urls.length) problems.push(`line ${i + 1}: list item cites no permalink: ${line.trim().slice(0, 60)}`);\n });\n return problems;\n}\n\nif (import.meta.url === pathToFileURL(process.argv[1] ?? \"\").href) {\n const args = process.argv.slice(2);\n const after = (flag) => { const i = args.indexOf(flag); if (i < 0) return []; const out = []; for (let j = i + 1; j < args.length && !args[j].startsWith(\"--\"); j++) out.push(args[j]); return out; };\n const problems = [];\n const [profile] = after(\"--profile\");\n if (profile) problems.push(...citationProblems(readFileSync(profile, \"utf8\"), after(\"--facts\").map((f) => JSON.parse(readFileSync(f, \"utf8\")))));\n problems.forEach((p) => console.log(p));\n process.exit(problems.length ? 1 : 0);\n}\n"};
|
|
@@ -31,20 +31,20 @@ export const realRun = async (args) => (await exec("gh", args, { maxBuffer: 1 <<
|
|
|
31
31
|
const ATTEMPTS = 3;
|
|
32
32
|
|
|
33
33
|
async function search(run, q, withState = false) {
|
|
34
|
-
const args = ["api", "graphql", "--paginate", "--
|
|
34
|
+
const args = ["api", "graphql", "--paginate", "--jq", ".data.search", "-f", `query=${searchQuery(withState)}`, "-f", `q=${q}`];
|
|
35
35
|
let out;
|
|
36
36
|
for (let attempt = 1; ; attempt++) {
|
|
37
37
|
try {
|
|
38
38
|
out = await run(args);
|
|
39
39
|
break;
|
|
40
40
|
} catch (e) {
|
|
41
|
-
if (attempt === ATTEMPTS
|
|
41
|
+
if (attempt === ATTEMPTS) throw e;
|
|
42
42
|
await new Promise((r) => setTimeout(r, 1000 * attempt + Math.random() * 1000));
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
const pages = JSON.parse(
|
|
46
|
-
const nodes = pages.flatMap((p) => p.
|
|
47
|
-
return { total: pages[0].
|
|
45
|
+
const pages = out.split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
46
|
+
const nodes = pages.flatMap((p) => p.nodes).filter((n) => n?.url);
|
|
47
|
+
return { total: pages[0].issueCount, nodes };
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const item = (n) => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.66",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Bot-side code shared by every hub agent: registry tools, the roster hook, the eve channel, the host sandbox and browser, and model helpers.",
|
|
6
6
|
"type": "module",
|
package/world.mjs
CHANGED
|
@@ -27048,7 +27048,7 @@ var HostedUpdate = object2({
|
|
|
27048
27048
|
/** `updated`: the move was a commit that is not an update to the hub's release (a memory or instructions edit); `commit` is it. */
|
|
27049
27049
|
plain: boolean5().optional(),
|
|
27050
27050
|
/**
|
|
27051
|
-
* `kind`: sign-in (the owner signs in again) | github (the owner approves GitHub: the app asks `githubConsent` for a fresh link) | build (`url` is the failed build) | other. A failed build also carries
|
|
27051
|
+
* `kind`: sign-in (the owner signs in again) | github (the owner approves GitHub: the app asks `githubConsent` for a fresh link) | build (`url` is the failed build) | no-repo (permanent, no Retry) | other. A failed build also carries
|
|
27052
27052
|
* what Vercel says of it once the hub has read it: the `step` that failed, the `sha` it built, and the `log`'s last lines.
|
|
27053
27053
|
*/
|
|
27054
27054
|
error: maybe(object2({
|
|
@@ -27201,7 +27201,7 @@ var Upgrade = external_exports.object({
|
|
|
27201
27201
|
ref: external_exports.string().nullable().default(null),
|
|
27202
27202
|
deployment: external_exports.string().nullable().default(null),
|
|
27203
27203
|
at: external_exports.string(),
|
|
27204
|
-
error: external_exports.object({ kind: external_exports.enum(["sign-in", "github", "other"]), message: external_exports.string() }).nullable().default(null)
|
|
27204
|
+
error: external_exports.object({ kind: external_exports.enum(["sign-in", "github", "no-repo", "other"]), message: external_exports.string() }).nullable().default(null)
|
|
27205
27205
|
});
|
|
27206
27206
|
var Failure = external_exports.object({ step: external_exports.string().nullable(), message: external_exports.string().nullable(), log: external_exports.array(external_exports.string()) });
|
|
27207
27207
|
var Attempt = external_exports.object({ id: external_exports.string().nullable(), key: external_exports.string().nullable(), state: external_exports.string(), url: external_exports.string(), failure: Failure.nullable().default(null) });
|