eve-hub 0.16.11 → 0.16.12
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/kit-change.d.mts +3 -0
- package/kit-change.mjs +66 -0
- package/kit-store.d.mts +6 -0
- package/kit-store.mjs +14 -0
- package/package.json +11 -2
- package/update.d.mts +4 -0
- package/update.mjs +82 -0
package/kit-change.d.mts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export type KitUpdate = { files: Record<string, string>; deletions: string[]; reported: string[] };
|
|
2
|
+
export declare class KitChangeError extends Error {}
|
|
3
|
+
export declare function kitChange(source: { paths: string[]; read: (paths: string[]) => Promise<Map<string, string>> }): Promise<KitUpdate>;
|
package/kit-change.mjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// What a kit update changes in a workspace repo, read from any source of its files: the hosted hub reads them over
|
|
2
|
+
// GitHub's API (hosted-hub/src/onboard/repo-bump.ts), `eve-hub update` (update.mjs) from a checkout. syncKit's kit-file
|
|
3
|
+
// changes, the `eve-hub/world` line dropped from each `agent.ts`, and each hosted bot's `sandbox.ts` moved to one
|
|
4
|
+
// `git__gh` runs in (migrateSandbox). Customized files are left alone and reported.
|
|
5
|
+
import { isKitFile, syncKit } from "./sync-kit.mjs";
|
|
6
|
+
import { migrateSandbox } from "./sandbox-migrate.mjs";
|
|
7
|
+
import { agentRunsOn, parseWorkspace, WorkspaceError, WORKSPACE_FILE } from "./workspace.mjs";
|
|
8
|
+
import KIT_MODULES from "./kit-modules.json" with { type: "json" };
|
|
9
|
+
|
|
10
|
+
const AGENT_FILE = /^agents\/([^/]+)\/agent\/(.+)$/;
|
|
11
|
+
|
|
12
|
+
// The line workspaces made before kit 0.13.0 have in `agent.ts`. The kit no longer exports `eve-hub/world`, so a build that keeps it fails.
|
|
13
|
+
const HUB_WORLD_LINE = /^[ \t]*experimental:\s*\{\s*workflow:\s*\{\s*world:\s*(["'])eve-hub\/world\1\s*\}\s*\},?[ \t]*\r?\n/m;
|
|
14
|
+
|
|
15
|
+
// A repo a person must repair before the kit can update it; the message says what to fix.
|
|
16
|
+
export class KitChangeError extends Error {}
|
|
17
|
+
|
|
18
|
+
// `paths` are every file in the repo, workspace-relative; `read(paths)` returns their text by path.
|
|
19
|
+
export async function kitChange({ paths, read }) {
|
|
20
|
+
if (!paths.includes(WORKSPACE_FILE)) throw new KitChangeError(`no ${WORKSPACE_FILE}`);
|
|
21
|
+
const definition = definitionOf((await read([WORKSPACE_FILE])).get(WORKSPACE_FILE));
|
|
22
|
+
const isAgentFile = (path) => /(?:^|\/)agent\.ts$/.test(AGENT_FILE.exec(path)?.[2] ?? "");
|
|
23
|
+
const hostedSandbox = (path) => {
|
|
24
|
+
const m = AGENT_FILE.exec(path);
|
|
25
|
+
return m?.[2] === "sandbox.ts" && definition.agents.some((a) => a.name === m[1]) && agentRunsOn(definition, m[1]) === "hosted";
|
|
26
|
+
};
|
|
27
|
+
const found = paths.filter((p) => isKitFile(AGENT_FILE.exec(p)?.[2] ?? "") || isAgentFile(p) || hostedSandbox(p));
|
|
28
|
+
const text = await read(found);
|
|
29
|
+
const files = new Map(definition.agents.map((a) => [a.name, {}]));
|
|
30
|
+
const agentFiles = [], sandboxes = [];
|
|
31
|
+
for (const p of found) {
|
|
32
|
+
const [, bot, file] = AGENT_FILE.exec(p);
|
|
33
|
+
if (!files.has(bot)) continue;
|
|
34
|
+
if (isAgentFile(p)) agentFiles.push({ path: p, text: text.get(p) });
|
|
35
|
+
if (hostedSandbox(p)) sandboxes.push({ path: p, text: text.get(p) });
|
|
36
|
+
else if (isKitFile(file)) files.get(bot)[file] = text.get(p);
|
|
37
|
+
}
|
|
38
|
+
const tiersOf = (name) => Object.keys(definition.agents.find((a) => a.name === name)?.subagents ?? {});
|
|
39
|
+
const bots = [...files].map(([name, f]) => ({ name, runsOn: agentRunsOn(definition, name), files: f, subagents: tiersOf(name) }));
|
|
40
|
+
const change = syncKit({ bots, modules: KIT_MODULES });
|
|
41
|
+
const path = (bot, file) => `agents/${bot}/agent/${file}`;
|
|
42
|
+
const world = agentFiles.filter((f) => f.text.includes("eve-hub/world")).map((f) => ({ path: f.path, text: f.text.replace(HUB_WORLD_LINE, "") }));
|
|
43
|
+
const moved = sandboxes.map((f) => ({ path: f.path, text: f.text, ...migrateSandbox(f.text) }));
|
|
44
|
+
return {
|
|
45
|
+
files: {
|
|
46
|
+
...Object.fromEntries(change.write.map((w) => [path(w.bot, w.file), w.content])),
|
|
47
|
+
...Object.fromEntries(world.filter((f) => !f.text.includes("eve-hub/world")).map((f) => [f.path, f.text])),
|
|
48
|
+
...Object.fromEntries(moved.filter((f) => f.source !== undefined && f.source !== f.text).map((f) => [f.path, f.source])),
|
|
49
|
+
},
|
|
50
|
+
deletions: change.delete.map((d) => path(d.bot, d.file)),
|
|
51
|
+
reported: [
|
|
52
|
+
...change.report.map((r) => `${path(r.bot, r.file)} ${r.reason}`),
|
|
53
|
+
...world.filter((f) => f.text.includes("eve-hub/world")).map((f) => `${f.path} still imports eve-hub/world, which the kit no longer has`),
|
|
54
|
+
...moved.filter((f) => f.reason).map((f) => `${f.path} ${f.reason}`),
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function definitionOf(text) {
|
|
60
|
+
let json;
|
|
61
|
+
try { json = JSON.parse(text); } catch { throw new KitChangeError(`${WORKSPACE_FILE} is not valid JSON; fix it, then retry`); }
|
|
62
|
+
try { return parseWorkspace(json); } catch (e) {
|
|
63
|
+
if (e instanceof WorkspaceError) throw new KitChangeError(`${e.message}; fix it, then retry`);
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
}
|
package/kit-store.d.mts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const KIT_STORE: string;
|
|
2
|
+
export declare const KIT_LATEST: string;
|
|
3
|
+
export declare function kitTarball(version: string): string;
|
|
4
|
+
export declare function kitTarballUrl(version: string): string;
|
|
5
|
+
export declare function kitLatestUrl(): string;
|
|
6
|
+
export declare function pinnedVersion(pin: string | undefined): string | undefined;
|
package/kit-store.mjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Where each eve-hub version's tarball is served: a public Vercel Blob store (eve-hub-kit, on vercel-internal-playground),
|
|
2
|
+
// one immutable file per version, uploaded by scripts/publish.mjs before the npm publish. Hosted workspaces pin this URL,
|
|
3
|
+
// so nothing a deploy waits on reads eve-hub from npm, whose publish-time malware scan can hold a new version 15 minutes or more.
|
|
4
|
+
// `latest.json` beside them names the newest release; a workspace's eve-hub-update workflow reads it.
|
|
5
|
+
export const KIT_STORE = "https://fql3r3ljskj7rzcd.public.blob.vercel-storage.com";
|
|
6
|
+
export const kitTarball = (version) => `eve-hub/eve-hub-${version}.tgz`;
|
|
7
|
+
export const kitTarballUrl = (version) => `${KIT_STORE}/${kitTarball(version)}`;
|
|
8
|
+
export const KIT_LATEST = "eve-hub/latest.json";
|
|
9
|
+
export const kitLatestUrl = () => `${KIT_STORE}/${KIT_LATEST}`;
|
|
10
|
+
|
|
11
|
+
// The version a pin names: a tarball URL from the store or a plain version; undefined for anything else (a link, a range).
|
|
12
|
+
export function pinnedVersion(pin) {
|
|
13
|
+
return /(?:^|\/eve-hub-)(\d+\.\d+\.\d+)(?:\.tgz)?$/.exec(pin ?? "")?.[1];
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eve-hub",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.12",
|
|
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",
|
|
7
|
+
"bin": {
|
|
8
|
+
"eve-hub": "./update.mjs"
|
|
9
|
+
},
|
|
7
10
|
"exports": {
|
|
8
11
|
"./package.json": "./package.json",
|
|
9
12
|
"./tools/*": "./tools/*.js",
|
|
@@ -30,7 +33,10 @@
|
|
|
30
33
|
"./bot-token": "./bot-token.mjs",
|
|
31
34
|
"./hub-oidc": "./hub-oidc.mjs",
|
|
32
35
|
"./prepare": "./prepare.mjs",
|
|
33
|
-
"./vercel": "./vercel.mjs"
|
|
36
|
+
"./vercel": "./vercel.mjs",
|
|
37
|
+
"./kit-change": "./kit-change.mjs",
|
|
38
|
+
"./kit-store": "./kit-store.mjs",
|
|
39
|
+
"./update": "./update.mjs"
|
|
34
40
|
},
|
|
35
41
|
"peerDependencies": {
|
|
36
42
|
"@vercel/connect": "^2.4.1",
|
|
@@ -47,5 +53,8 @@
|
|
|
47
53
|
"smol-toml": "^1.9.1",
|
|
48
54
|
"tree-sitter-bash": "0.25.1",
|
|
49
55
|
"web-tree-sitter": "0.27.1"
|
|
56
|
+
},
|
|
57
|
+
"hubPins": {
|
|
58
|
+
"eve": "0.88.0"
|
|
50
59
|
}
|
|
51
60
|
}
|
package/update.d.mts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type UpdateResult = { version: string; from: string | null; current: boolean; changed: string[]; deleted: string[]; left: { path: string; reason: string }[] };
|
|
2
|
+
export declare function update(options?: { dir?: string; version?: string; eve?: string }): Promise<UpdateResult>;
|
|
3
|
+
export declare function summary(result: UpdateResult): string;
|
|
4
|
+
export declare function commitRequest(result: UpdateResult, at: { dir?: string; repo: string; branch: string; head: string }): unknown;
|
package/update.mjs
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `eve-hub update`, run in a workspace checkout by its eve-hub-update workflow (hosted-hub/lib/workspace-template.mjs)
|
|
3
|
+
// as `npx --yes <this version's tarball URL> update`: moves the repo to this kit. It applies kitChange (kit-change.mjs)
|
|
4
|
+
// to the files on disk and pins `eve-hub` to this version's tarball, and `eve` to the hub's (`hubPins.eve`, stamped by
|
|
5
|
+
// scripts/pack.mjs). Customized files stay as they are and are reported. A repo already on this version is left alone.
|
|
6
|
+
// `--json` prints the result; `--summary <file>` appends what changed (summary); `--commit <file> --repo <o/n>` writes
|
|
7
|
+
// the GitHub request that commits the update onto the checkout's branch (commitRequest).
|
|
8
|
+
import { execFileSync } from "node:child_process";
|
|
9
|
+
import { appendFileSync, readdirSync, readFileSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { dirname, join, relative } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
import { kitChange, KitChangeError } from "./kit-change.mjs";
|
|
14
|
+
import { kitTarballUrl, pinnedVersion } from "./kit-store.mjs";
|
|
15
|
+
|
|
16
|
+
const SKIPPED = new Set([".git", "node_modules", ".eve", ".vercel", ".hub"]);
|
|
17
|
+
const OWN = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "package.json"), "utf8"));
|
|
18
|
+
|
|
19
|
+
function filesIn(root, dir = root) {
|
|
20
|
+
return readdirSync(dir, { withFileTypes: true }).flatMap((e) => SKIPPED.has(e.name) ? []
|
|
21
|
+
: e.isDirectory() ? filesIn(root, join(dir, e.name))
|
|
22
|
+
: e.isFile() ? [relative(root, join(dir, e.name)).split("\\").join("/")] : []);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function update({ dir = process.cwd(), version = OWN.version, eve = OWN.hubPins?.eve } = {}) {
|
|
26
|
+
const pkgFile = join(dir, "package.json");
|
|
27
|
+
const pkg = JSON.parse(readFileSync(pkgFile, "utf8"));
|
|
28
|
+
const pin = kitTarballUrl(version);
|
|
29
|
+
const current = pkg.dependencies?.["eve-hub"];
|
|
30
|
+
const from = pinnedVersion(current);
|
|
31
|
+
const result = { version, from: from ?? current ?? null, changed: [], deleted: [], left: [] };
|
|
32
|
+
if (current === pin) return { ...result, current: true };
|
|
33
|
+
const change = await kitChange({ paths: filesIn(dir), read: async (paths) => new Map(paths.map((p) => [p, readFileSync(join(dir, p), "utf8")])) });
|
|
34
|
+
for (const [path, text] of Object.entries(change.files)) {
|
|
35
|
+
mkdirSync(dirname(join(dir, path)), { recursive: true });
|
|
36
|
+
writeFileSync(join(dir, path), text);
|
|
37
|
+
}
|
|
38
|
+
for (const path of change.deletions) rmSync(join(dir, path), { force: true });
|
|
39
|
+
writeFileSync(pkgFile, `${JSON.stringify({ ...pkg, dependencies: { ...pkg.dependencies, "eve-hub": pin, ...(eve === undefined ? {} : { eve }) } }, null, 2)}\n`);
|
|
40
|
+
const left = change.reported.map((r) => { const i = r.indexOf(" "); return { path: r.slice(0, i), reason: r.slice(i + 1) }; });
|
|
41
|
+
return { ...result, current: false, changed: ["package.json", ...Object.keys(change.files)].sort(), deleted: change.deletions.sort(), left };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function summary(r) {
|
|
45
|
+
const list = (items) => items.map((i) => `- \`${i}\``).join("\n");
|
|
46
|
+
return [
|
|
47
|
+
`Moves this workspace from eve-hub ${r.from ?? "(unpinned)"} to ${r.version}.`,
|
|
48
|
+
r.changed.length ? `\nChanged:\n${list(r.changed)}` : "",
|
|
49
|
+
r.deleted.length ? `\nDeleted:\n${list(r.deleted)}` : "",
|
|
50
|
+
r.left.length ? `\nKept as you customized them, so this release's version of them is not applied:\n${r.left.map((l) => `- \`${l.path}\` ${l.reason}`).join("\n")}` : "",
|
|
51
|
+
].join("\n").trim() + "\n";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// The GraphQL `createCommitOnBranch` request that commits the update onto `branch` if it is still at `head`. GitHub creates
|
|
55
|
+
// and signs that commit, so a repo whose rules require signed commits takes it; a commit made in the checkout and pushed is unsigned.
|
|
56
|
+
export function commitRequest(r, { dir = process.cwd(), repo, branch, head }) {
|
|
57
|
+
return {
|
|
58
|
+
query: "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { url } } }",
|
|
59
|
+
variables: { input: { branch: { repositoryNameWithOwner: repo, branchName: branch }, expectedHeadOid: head, message: { headline: `eve-hub@${r.version}`, body: summary(r) },
|
|
60
|
+
fileChanges: { additions: r.changed.map((path) => ({ path, contents: readFileSync(join(dir, path)).toString("base64") })), deletions: r.deleted.map((path) => ({ path })) } } },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === (await import("node:fs")).realpathSync(process.argv[1])) {
|
|
65
|
+
const [command, ...args] = process.argv.slice(2);
|
|
66
|
+
const arg = (name) => { const i = args.indexOf(name); return i >= 0 ? args[i + 1] : undefined; };
|
|
67
|
+
if (command !== "update" || (arg("--commit") !== undefined && !arg("--repo"))) { console.error("usage: eve-hub update [--json] [--summary <file>] [--commit <file> --repo <owner/name>]"); process.exit(2); }
|
|
68
|
+
try {
|
|
69
|
+
const r = await update();
|
|
70
|
+
if (arg("--summary")) appendFileSync(arg("--summary"), summary(r));
|
|
71
|
+
if (arg("--commit") && !r.current) {
|
|
72
|
+
const git = (...a) => execFileSync("git", a, { encoding: "utf8" }).trim();
|
|
73
|
+
writeFileSync(arg("--commit"), JSON.stringify(commitRequest(r, { repo: arg("--repo"), branch: git("branch", "--show-current"), head: git("rev-parse", "HEAD") })));
|
|
74
|
+
}
|
|
75
|
+
if (args.includes("--json")) console.log(JSON.stringify(r));
|
|
76
|
+
else console.log(r.current ? `already on eve-hub ${r.version}` : `eve-hub ${r.from} -> ${r.version}: ${r.changed.length + r.deleted.length} files changed, ${r.left.length} kept as customized`);
|
|
77
|
+
} catch (e) {
|
|
78
|
+
if (!(e instanceof KitChangeError)) throw e;
|
|
79
|
+
console.error(e.message);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|