foldrun 0.9.0 → 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/bin/foldrun.mjs +21 -7
- package/docs/agents.md +2 -1
- package/docs/api-usage.md +16 -2
- package/docs/api.md +4 -2
- package/docs/backups-and-restore.md +35 -66
- package/docs/budgets-and-billing.md +1 -1
- package/docs/cli.md +13 -5
- package/docs/hooks.md +139 -0
- package/docs/notifications.md +7 -1
- package/docs/security.md +14 -0
- package/docs/tools.md +30 -5
- package/docs/versions.md +39 -2
- package/package.json +3 -3
- package/src/commands.mjs +481 -17
- package/src/guide.mjs +1 -1
- package/src/terminal.mjs +85 -0
package/src/guide.mjs
CHANGED
|
@@ -30,7 +30,7 @@ export function agentRulesBlock() {
|
|
|
30
30
|
|
|
31
31
|
# Read foldrun's docs before you edit this folder
|
|
32
32
|
|
|
33
|
-
This is a foldrun account: agents, flows, tools and knowledge as markdown, run by foldrun. Its formats — frontmatter fields, step options, \`verify:\` prefixes, which folder a file goes in — are foldrun's own and may differ from your training data. Before you write or change an agent, flow, tool or skill, read the relevant guide: \`foldrun docs\` lists them and \`foldrun docs <page>\` prints one (start with \`foldrun docs coding-agents\`). They ship with the CLI, so they match the version you are driving. Check every edit with \`foldrun check\` — offline, no model calls. For a flow's shape, \`foldrun flow show <flow>\` draws it, and \`foldrun flow add\`, \`foldrun flow rm-step\`, \`foldrun flow dup-step\` and \`foldrun agent link\` make the same edits as the dashboard canvas (patterns, sub-agents, consults, \`ask\`). A script tool's own \`secrets: [NAMES]\` are granted with the tool; list on an agent only the secrets it uses directly. Paths to the workspace's own folders are written \`workspace/storage/…\` and \`workspace/state/…\` — in an agent's prose, a flow's options and a script's arguments — not \`../../\`, which still works but is the older spelling.
|
|
33
|
+
This is a foldrun account: agents, flows, tools and knowledge as markdown, run by foldrun. Its formats — frontmatter fields, step options, \`verify:\` prefixes, which folder a file goes in — are foldrun's own and may differ from your training data. Before you write or change an agent, flow, tool or skill, read the relevant guide: \`foldrun docs\` lists them and \`foldrun docs <page>\` prints one (start with \`foldrun docs coding-agents\`). They ship with the CLI, so they match the version you are driving. Check every edit with \`foldrun check\` — offline, no model calls. For a flow's shape, \`foldrun flow show <flow>\` draws it, and \`foldrun flow add\`, \`foldrun flow rm-step\`, \`foldrun flow dup-step\` and \`foldrun agent link\` make the same edits as the dashboard canvas (patterns, sub-agents, consults, \`ask\`). A script tool's own \`secrets: [NAMES]\` are granted with the tool; list on an agent only the secrets it uses directly. A hook (\`hooks/<name>/hook.md\` + \`run.mjs\`, turned on with \`hooks: [name]\`) is code at every tool call, step start and step end — \`foldrun hook add\`/\`test\` and \`foldrun docs hooks\`. Paths to the workspace's own folders are written \`workspace/storage/…\` and \`workspace/state/…\` — in an agent's prose, a flow's options and a script's arguments — not \`../../\`, which still works but is the older spelling.
|
|
34
34
|
|
|
35
35
|
This block is for coding tools. It is written and re-added by the foldrun CLI, and the runtime strips it, so the agents that run here never see it; the rest of this file is the account's own context, which they do read. Committing the block with your work keeps the tree clean.
|
|
36
36
|
|
package/src/terminal.mjs
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// What reaches a person's terminal.
|
|
2
|
+
//
|
|
3
|
+
// The CLI prints what a platform sends — run replies, logs, file contents,
|
|
4
|
+
// release notes, error messages — and a terminal obeys escape sequences in
|
|
5
|
+
// it: move the cursor and overwrite a line, clear the screen, retitle the
|
|
6
|
+
// window, write the clipboard (OSC 52), turn text into a link that goes
|
|
7
|
+
// somewhere else (OSC 8). A hostile or broken platform, or an agent's reply
|
|
8
|
+
// on a compromised one, could do any of that to the person running the CLI.
|
|
9
|
+
//
|
|
10
|
+
// So everything written to stdout and stderr passes through terminalSafe():
|
|
11
|
+
// the only escape sequences let through are the colours this CLI paints
|
|
12
|
+
// itself (bold, dim, red, green, yellow, reset), and every other control
|
|
13
|
+
// character goes, except newline and tab. Covering the streams rather than
|
|
14
|
+
// each print means a new command cannot forget — and a pipe is covered too,
|
|
15
|
+
// because `foldrun logs | cat` still ends on a screen.
|
|
16
|
+
//
|
|
17
|
+
// The one exception is content a person asked for byte for byte: `source
|
|
18
|
+
// cat`, `storage cat`, a download to stdout, a CSV. Those go through
|
|
19
|
+
// writeExact(), untouched into a pipe or a file (`foldrun source cat x > x`
|
|
20
|
+
// stays exact) and still filtered when they land on a terminal. `--json` needs
|
|
21
|
+
// no exception: JSON.stringify writes control characters as \u escapes.
|
|
22
|
+
|
|
23
|
+
/** The SGR parameters paint() in commands.mjs uses. Nothing else is needed:
|
|
24
|
+
* conceal (8) or reverse (7) from a platform could hide or fake text. */
|
|
25
|
+
const OUR_SGR = new Set(["", "0", "1", "2", "31", "32", "33"]);
|
|
26
|
+
|
|
27
|
+
// ESC [ … final byte (CSI); ESC ] … BEL or ESC \ (OSC); ESC P / ^ / _ … ESC \
|
|
28
|
+
// (DCS, PM, APC); ESC and one more character (everything else); a lone ESC.
|
|
29
|
+
const ESCAPES = /\u001b\[([0-?]*)[ -/]*([@-~])|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?|\u001b[P^_][^\u001b]*(?:\u001b\\)?|\u001b[ -~]?/g;
|
|
30
|
+
// C0 except tab and newline, DEL, and C1 (U+0080–U+009F: 0x9B is a one-byte CSI).
|
|
31
|
+
const CONTROLS = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f]/;
|
|
32
|
+
// One pass, escape sequences first, so a kept colour is not then stripped
|
|
33
|
+
// of its ESC as a control character.
|
|
34
|
+
const UNSAFE = new RegExp(`${ESCAPES.source}|${CONTROLS.source}`, "g");
|
|
35
|
+
|
|
36
|
+
/** @typedef {NodeJS.WriteStream & { __foldrunGuarded?: boolean, __foldrunRawWrite?: (chunk: string | Uint8Array) => boolean }} Guardable */
|
|
37
|
+
|
|
38
|
+
/** Text made safe for a terminal: this CLI's colours kept, every other
|
|
39
|
+
* escape sequence and control character removed. `\r\n` becomes `\n`. */
|
|
40
|
+
export function terminalSafe(text) {
|
|
41
|
+
return String(text)
|
|
42
|
+
.replace(/\r\n/g, "\n")
|
|
43
|
+
.replace(UNSAFE, (seq, params, final) => {
|
|
44
|
+
if (final === "m" && seq.startsWith("\u001b[") && String(params).split(";").every((p) => OUR_SGR.has(p))) return seq;
|
|
45
|
+
return "";
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Put terminalSafe() in front of a stream's write. Returns whether it did
|
|
51
|
+
* (false the second time). A Buffer is decoded as UTF-8 first: bytes meant
|
|
52
|
+
* for a file have no business on a screen as they are.
|
|
53
|
+
* @param {Guardable} stream
|
|
54
|
+
*/
|
|
55
|
+
export function guardStream(stream) {
|
|
56
|
+
if (!stream || stream.__foldrunGuarded) return false;
|
|
57
|
+
const write = stream.write.bind(stream);
|
|
58
|
+
stream.__foldrunRawWrite = write;
|
|
59
|
+
/** @param {string | Uint8Array} chunk @param {unknown} [encoding] @param {(err?: Error | null) => void} [cb] */
|
|
60
|
+
const guarded = (chunk, encoding, cb) => {
|
|
61
|
+
const done = typeof encoding === "function" ? /** @type {(err?: Error | null) => void} */ (encoding) : cb;
|
|
62
|
+
const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
63
|
+
return write(terminalSafe(text), done);
|
|
64
|
+
};
|
|
65
|
+
stream.write = /** @type {typeof stream.write} */ (guarded);
|
|
66
|
+
stream.__foldrunGuarded = true;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Guard stdout and stderr. Once, at start-up. */
|
|
71
|
+
export function guardTerminal() {
|
|
72
|
+
guardStream(process.stdout);
|
|
73
|
+
guardStream(process.stderr);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Content a person asked for exactly — a file, a download, a CSV: byte for
|
|
78
|
+
* byte into a pipe or a file, filtered like everything else on a terminal.
|
|
79
|
+
* @param {string | Uint8Array} chunk
|
|
80
|
+
* @param {Guardable} [stream]
|
|
81
|
+
*/
|
|
82
|
+
export function writeExact(chunk, stream = process.stdout) {
|
|
83
|
+
if (stream.isTTY || !stream.__foldrunRawWrite) return stream.write(chunk);
|
|
84
|
+
return stream.__foldrunRawWrite(chunk);
|
|
85
|
+
}
|