baesian 0.0.0-stage → 1.0.0
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/LICENSE.md +105 -0
- package/README.md +217 -2
- package/dist/agents.js +249 -0
- package/dist/api.js +13 -0
- package/dist/bin.js +14 -0
- package/dist/ci.js +82 -0
- package/dist/cite.js +66 -0
- package/dist/cli.js +831 -0
- package/dist/client/assets/alegreya-cyrillic-ext-wght-italic-DzWRXZb0.woff2 +0 -0
- package/dist/client/assets/alegreya-cyrillic-ext-wght-normal-CN0-BfWD.woff2 +0 -0
- package/dist/client/assets/alegreya-cyrillic-wght-italic-Dhrv2xfk.woff2 +0 -0
- package/dist/client/assets/alegreya-cyrillic-wght-normal-CU5lb38f.woff2 +0 -0
- package/dist/client/assets/alegreya-greek-ext-wght-italic-CoUI9EEu.woff2 +0 -0
- package/dist/client/assets/alegreya-greek-ext-wght-normal-C8PPRmwJ.woff2 +0 -0
- package/dist/client/assets/alegreya-greek-wght-italic-CVt9-Jr4.woff2 +0 -0
- package/dist/client/assets/alegreya-greek-wght-normal-BPx8yWcZ.woff2 +0 -0
- package/dist/client/assets/alegreya-latin-ext-wght-italic-BIuo_2sR.woff2 +0 -0
- package/dist/client/assets/alegreya-latin-ext-wght-normal-o8axeAGu.woff2 +0 -0
- package/dist/client/assets/alegreya-latin-wght-italic-Bxkcgmge.woff2 +0 -0
- package/dist/client/assets/alegreya-latin-wght-normal-BG9rTJo4.woff2 +0 -0
- package/dist/client/assets/alegreya-vietnamese-wght-italic-BAPuWgnQ.woff2 +0 -0
- package/dist/client/assets/alegreya-vietnamese-wght-normal-DOV9JYup.woff2 +0 -0
- package/dist/client/assets/archivo-latin-ext-wdth-normal-7khWdh9v.woff2 +0 -0
- package/dist/client/assets/archivo-latin-wdth-normal-DY7AcnAa.woff2 +0 -0
- package/dist/client/assets/archivo-vietnamese-wdth-normal-rJmnGBSt.woff2 +0 -0
- package/dist/client/assets/index-B3CFhLg-.css +1 -0
- package/dist/client/assets/index-DUcPUPI_.js +15 -0
- package/dist/client/index.html +15 -0
- package/dist/facts.js +278 -0
- package/dist/folder-storage.js +71 -0
- package/dist/frontmatter.js +38 -0
- package/dist/gate-paths.js +77 -0
- package/dist/gate.js +237 -0
- package/dist/git.js +100 -0
- package/dist/globs.js +120 -0
- package/dist/hookblock.js +146 -0
- package/dist/hooks.js +911 -0
- package/dist/ids.js +23 -0
- package/dist/login.js +301 -0
- package/dist/mcp.js +303 -0
- package/dist/merges.js +849 -0
- package/dist/modes.js +245 -0
- package/dist/notify.js +91 -0
- package/dist/ops.js +381 -0
- package/dist/passkey.js +86 -0
- package/dist/personal.js +163 -0
- package/dist/practice-answers.js +42 -0
- package/dist/proposals.js +197 -0
- package/dist/quote.js +28 -0
- package/dist/report.js +261 -0
- package/dist/routing.js +411 -0
- package/dist/schema.js +1178 -0
- package/dist/server.js +514 -0
- package/dist/similarity.js +45 -0
- package/dist/sshsig.js +159 -0
- package/dist/state.js +135 -0
- package/dist/storage.js +61 -0
- package/dist/store.js +2259 -0
- package/dist/style.js +408 -0
- package/dist/sync.js +797 -0
- package/dist/trailers.js +121 -0
- package/dist/trust.js +222 -0
- package/dist/usage-fields.js +161 -0
- package/dist/vault.js +849 -0
- package/dist/verify.js +443 -0
- package/dist/version.js +70 -0
- package/dist/wait.js +449 -0
- package/dist/wikilinks.js +25 -0
- package/package.json +71 -4
package/dist/ci.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { asWritten } from "./schema.js";
|
|
3
|
+
import { TRUST_ENV, checkSigned, projectLine } from "./trust.js";
|
|
4
|
+
/** A file's JSON on one line, its fields in the file's own order: a passkey's line as the website showed it. */
|
|
5
|
+
const compact = (store, rel) => JSON.stringify(JSON.parse(store.bytes(rel).toString("utf8")));
|
|
6
|
+
/**
|
|
7
|
+
* The pin for a linked repo (B-pt52b), from the live layer only: the line naming its project (B-mar8x); each service key
|
|
8
|
+
* and revocation the relay sent; and each passkey key file whose `.sig` checks against those keys (`checkSigned()`), at
|
|
9
|
+
* the time it was enrolled. Or why there's none yet: the relay sends its key with the project's first file. `spare` is the
|
|
10
|
+
* service's spare keys (B-x47ek), from the service itself (`spareKeys()`), each pinned beside the keys unless the relay
|
|
11
|
+
* already sent it as one: with it pinned, CI keeps passing when the service switches to it.
|
|
12
|
+
*/
|
|
13
|
+
export function ciPin(store, service, project, spare = []) {
|
|
14
|
+
const files = store.trustFiles();
|
|
15
|
+
if (!files.service.length) {
|
|
16
|
+
return { problem: `this machine has no key from ${service} for project ${project} yet: the relay sends it with the project's first file. File a card, then run \`baesian ci\` again` };
|
|
17
|
+
}
|
|
18
|
+
const root = { service: files.service.map((s) => s.key), revoked: files.revoked.map((r) => r.key) };
|
|
19
|
+
const passkeys = [];
|
|
20
|
+
const signed = [];
|
|
21
|
+
const skipped = [];
|
|
22
|
+
for (const { file, key } of files.passkeys) {
|
|
23
|
+
const sig = store.storage.read(`${file}.sig`);
|
|
24
|
+
const r = sig === undefined ? { problem: "it has no signature" } : checkSigned(root, store.bytes(file), Buffer.from(sig).toString("utf8"), key.enrolled, file);
|
|
25
|
+
if ("problem" in r) {
|
|
26
|
+
skipped.push({ file, why: `${r.problem}, so the service didn't write it` });
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
passkeys.push({ file, handle: key.handle, account: key.account, enrolled: key.enrolled });
|
|
30
|
+
signed.push(compact(store, file));
|
|
31
|
+
}
|
|
32
|
+
const spares = spare.filter((k) => !files.service.some((s) => s.key.public_key === k.public_key));
|
|
33
|
+
const lines = [
|
|
34
|
+
projectLine(project),
|
|
35
|
+
...files.service.map((s) => compact(store, s.file)),
|
|
36
|
+
...spares.map((k) => JSON.stringify(asWritten("service-key", k))),
|
|
37
|
+
...files.revoked.map((r) => compact(store, r.file)),
|
|
38
|
+
...signed,
|
|
39
|
+
];
|
|
40
|
+
return { service, project, site: files.service[0].key.origin, lines, spare: spares.map((k) => k.key), passkeys, skipped };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The GitHub Actions steps that run `baesian verify` with the pin (B-pt52b), as this repo's own workflow runs it: the full
|
|
44
|
+
* history, which verify needs for the base, every commit in the range and the commit that linked the repo (B-45xz8); the
|
|
45
|
+
* pushed commits on a push, and the merge gate on a pull request (D31). The CLI comes from npm, pinned to `version`, the
|
|
46
|
+
* release that prints the steps (B-c0e0d): a new release changes a repo's CI only in a commit, made by running
|
|
47
|
+
* `baesian ci` again with it.
|
|
48
|
+
*/
|
|
49
|
+
export function workflowSteps(version) {
|
|
50
|
+
return ["# In a workflow that runs on pushes to your main branch and on pull requests, as one job's steps.", ...workflowBody(version)];
|
|
51
|
+
}
|
|
52
|
+
/** The steps alone, for `baesian ci`, which says where they go in a sentence of its own. */
|
|
53
|
+
export function workflowBody(version) {
|
|
54
|
+
const env = [" env:", ` ${TRUST_ENV}: \${{ vars.${TRUST_ENV} }}`];
|
|
55
|
+
const baesian = `npx --yes baesian@${version}`;
|
|
56
|
+
return [
|
|
57
|
+
"# Keep agents' commits from editing it, with a ruleset or code owners on .github/: it passes the pin to verify.",
|
|
58
|
+
"- uses: actions/checkout@v7",
|
|
59
|
+
" with:",
|
|
60
|
+
" fetch-depth: 0 # verify needs the base, every commit since it, and the commit that linked the repo",
|
|
61
|
+
"- uses: actions/setup-node@v7",
|
|
62
|
+
" with:",
|
|
63
|
+
" node-version: 22",
|
|
64
|
+
`# Baesian ${version} from npm, pinned: the release that printed these steps. To move CI to another, run \`baesian ci\` with it, and commit the steps it prints.`,
|
|
65
|
+
"- name: baesian verify (pushed commits)",
|
|
66
|
+
" if: github.event_name == 'push' && github.event.before != '0000000000000000000000000000000000000000'",
|
|
67
|
+
` run: ${baesian} verify --base "\${{ github.event.before }}"`,
|
|
68
|
+
...env,
|
|
69
|
+
"- name: baesian verify --merge (pull request)",
|
|
70
|
+
" if: github.event_name == 'pull_request'",
|
|
71
|
+
` run: ${baesian} verify --merge`,
|
|
72
|
+
...env,
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
/** Run a program with text on its standard input, as `baesian ci --github` runs `gh`. */
|
|
76
|
+
export function runProgram(command, args, opts) {
|
|
77
|
+
const r = spawnSync(command, args, { cwd: opts.cwd, input: opts.input, encoding: "utf8" });
|
|
78
|
+
if (r.error)
|
|
79
|
+
return { status: null, stdout: "", stderr: r.error.message, missing: r.error.code === "ENOENT" };
|
|
80
|
+
return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=ci.js.map
|
package/dist/cite.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Citations of Baesian's own decisions in what it prints (B-728jd): "(B-4rwyz)", "(D42, D144)". They're notes for
|
|
2
|
+
// whoever works on Baesian, and outside this repo they point nowhere, so ordinary output leaves them out and the global
|
|
3
|
+
// `--verbose` puts them back where they were. Each is marked where it's written, with `cite()` or `citeIn()`, never
|
|
4
|
+
// found by matching the finished text: a citation has the same shape as the user's own card IDs. The marks are two
|
|
5
|
+
// characters from Unicode's private use area, which nothing Baesian prints otherwise holds, around the citation and its
|
|
6
|
+
// punctuation; `unmark()` takes them out, with the citation or without it. Every place text leaves Baesian goes through
|
|
7
|
+
// it: the CLI's renderer (src/style.ts), `--json`, the MCP server, the local server's responses, and errors' messages
|
|
8
|
+
// (`Told`). No imports: the relay's store imports this too.
|
|
9
|
+
const OPEN = "";
|
|
10
|
+
const CLOSE = "";
|
|
11
|
+
const MARKED = /([^]*)/g;
|
|
12
|
+
/** A citation after a phrase: ` (B-4rwyz)`, or ` (D42, D144)`. Printed only with `--verbose`. */
|
|
13
|
+
export function cite(...ids) {
|
|
14
|
+
return `${OPEN} (${ids.join(", ")})${CLOSE}`;
|
|
15
|
+
}
|
|
16
|
+
/** A citation inside a parenthesis that says something else too: `(personal mode, B-4rwyz)`. */
|
|
17
|
+
export function citeIn(...ids) {
|
|
18
|
+
return `${OPEN}, ${ids.join(", ")}${CLOSE}`;
|
|
19
|
+
}
|
|
20
|
+
/** Text as it's printed: without its citations, or with them (`--verbose`), and never with their marks. */
|
|
21
|
+
export function unmark(text, verbose = false) {
|
|
22
|
+
return text.includes(OPEN) ? text.replace(MARKED, verbose ? "$1" : "") : text;
|
|
23
|
+
}
|
|
24
|
+
/** Whether a text has a citation in it. */
|
|
25
|
+
export const hasCitation = (text) => text.includes(OPEN);
|
|
26
|
+
/** A text in pieces: what it says, and its citations, for a renderer that styles them. */
|
|
27
|
+
export function segments(text) {
|
|
28
|
+
if (!text.includes(OPEN))
|
|
29
|
+
return [{ text, cite: false }];
|
|
30
|
+
const out = [];
|
|
31
|
+
let at = 0;
|
|
32
|
+
for (const m of text.matchAll(MARKED)) {
|
|
33
|
+
if (m.index > at)
|
|
34
|
+
out.push({ text: text.slice(at, m.index), cite: false });
|
|
35
|
+
out.push({ text: m[1], cite: true });
|
|
36
|
+
at = m.index + m[0].length;
|
|
37
|
+
}
|
|
38
|
+
if (at < text.length)
|
|
39
|
+
out.push({ text: text.slice(at), cite: false });
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/** A JSON replacer that unmarks every string, for `--json` and the MCP server's structured content. */
|
|
43
|
+
export function unmarkJson(_key, value) {
|
|
44
|
+
return typeof value === "string" ? unmark(value) : value;
|
|
45
|
+
}
|
|
46
|
+
/** A value with its strings unmarked, for output that isn't serialised here (the MCP server's structured content). */
|
|
47
|
+
export function unmarked(value) {
|
|
48
|
+
return JSON.parse(JSON.stringify(value, unmarkJson));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* An error whose message may cite Baesian's decisions and name what to do next. `message` is its plain text, without
|
|
52
|
+
* citations, with each step as a `next:` line, so anything that prints it (the MCP server, the local server, the relay)
|
|
53
|
+
* prints what an agent reads; `marked` keeps the citations, and `steps` the next steps, for the CLI to render.
|
|
54
|
+
*/
|
|
55
|
+
export class Told extends Error {
|
|
56
|
+
marked;
|
|
57
|
+
steps;
|
|
58
|
+
constructor(message, steps = []) {
|
|
59
|
+
super([unmark(message), ...steps.map(nextLine)].join("\n"));
|
|
60
|
+
this.marked = message;
|
|
61
|
+
this.steps = steps;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** A next step as plain text reads it: `next: <command> # why`. */
|
|
65
|
+
export const nextLine = (s) => `next: ${s.command}${s.why ? ` # ${s.why}` : ""}`;
|
|
66
|
+
//# sourceMappingURL=cite.js.map
|