@fastagent-sh/fastagent 0.13.0 → 0.14.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/README.md +2 -2
- package/dist/channels/feishu/context-buffer.d.ts +46 -0
- package/dist/channels/feishu/context-buffer.js +133 -0
- package/dist/channels/feishu/crypto.d.ts +4 -2
- package/dist/channels/feishu/crypto.js +4 -2
- package/dist/channels/feishu/feishu-api.d.ts +4 -4
- package/dist/channels/feishu/feishu-api.js +2 -2
- package/dist/channels/feishu/feishu.d.ts +11 -1
- package/dist/channels/feishu/feishu.js +143 -30
- package/dist/channels/feishu/invoke-turn.d.ts +19 -13
- package/dist/channels/feishu/invoke-turn.js +70 -19
- package/dist/channels/feishu/model.d.ts +97 -0
- package/dist/channels/feishu/model.js +9 -0
- package/dist/channels/feishu/normalize.d.ts +22 -0
- package/dist/channels/feishu/normalize.js +132 -0
- package/dist/channels/feishu/owned-threads.d.ts +11 -0
- package/dist/channels/feishu/owned-threads.js +47 -0
- package/dist/channels/feishu/parse.d.ts +20 -102
- package/dist/channels/feishu/parse.js +35 -145
- package/dist/channels/feishu/preview.js +2 -2
- package/dist/channels/feishu/scaffold/channel.ts +10 -3
- package/dist/channels/feishu/seen.js +12 -12
- package/dist/channels/lark/scaffold/channel.ts +10 -3
- package/dist/cli/commands/add.d.ts +9 -0
- package/dist/cli/commands/add.js +142 -0
- package/dist/cli/commands/chat.d.ts +3 -0
- package/dist/cli/commands/chat.js +16 -0
- package/dist/cli/commands/deploy.d.ts +13 -0
- package/dist/cli/commands/deploy.js +338 -0
- package/dist/cli/commands/dev.d.ts +11 -0
- package/dist/cli/commands/dev.js +76 -0
- package/dist/cli/commands/fire.d.ts +7 -0
- package/dist/cli/commands/fire.js +45 -0
- package/dist/cli/commands/info.d.ts +7 -0
- package/dist/cli/commands/info.js +108 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.js +81 -0
- package/dist/cli/commands/invoke.d.ts +7 -0
- package/dist/cli/commands/invoke.js +29 -0
- package/dist/cli/commands/login.d.ts +6 -0
- package/dist/cli/commands/login.js +63 -0
- package/dist/cli/commands/models.d.ts +1 -0
- package/dist/cli/commands/models.js +15 -0
- package/dist/cli/commands/schedule.d.ts +12 -0
- package/dist/cli/commands/schedule.js +89 -0
- package/dist/cli/commands/start.d.ts +10 -0
- package/dist/cli/commands/start.js +90 -0
- package/dist/cli/commands/tool.d.ts +1 -0
- package/dist/cli/commands/tool.js +37 -0
- package/dist/cli/fail.d.ts +19 -0
- package/dist/cli/fail.js +32 -0
- package/dist/cli/kernel.d.ts +89 -0
- package/dist/cli/kernel.js +190 -0
- package/dist/cli/program.d.ts +11 -0
- package/dist/cli/program.js +421 -0
- package/dist/cli/serve.d.ts +28 -0
- package/dist/cli/serve.js +90 -0
- package/dist/cli/shared.d.ts +24 -0
- package/dist/cli/shared.js +116 -0
- package/dist/cli.js +8 -1329
- package/dist/deploy/docker/plan.d.ts +45 -0
- package/dist/deploy/docker/plan.js +139 -0
- package/dist/deploy/docker/run.d.ts +40 -0
- package/dist/deploy/docker/run.js +126 -0
- package/dist/deploy/preflight.js +4 -3
- package/dist/deploy/runner.d.ts +4 -1
- package/dist/deploy/runner.js +1 -0
- package/dist/engines/pi/auth.js +160 -46
- package/dist/engines/pi/chat.js +77 -4
- package/dist/engines/pi/config.d.ts +12 -3
- package/dist/engines/pi/config.js +16 -1
- package/dist/engines/pi/create.d.ts +14 -5
- package/dist/engines/pi/create.js +44 -9
- package/dist/engines/pi/harness.d.ts +16 -1
- package/dist/engines/pi/harness.js +77 -1
- package/dist/engines/pi/invoke.d.ts +1 -1
- package/dist/engines/pi/invoke.js +37 -2
- package/dist/engines/pi/login.js +1 -1
- package/dist/engines/pi/search-tools.d.ts +10 -0
- package/dist/engines/pi/search-tools.js +138 -0
- package/dist/engines/pi/tool-context.d.ts +28 -0
- package/dist/engines/pi/tool-context.js +8 -0
- package/dist/engines/pi/tool.d.ts +32 -1
- package/dist/engines/pi/tool.js +42 -1
- package/dist/engines/pi/workspace.d.ts +4 -1
- package/dist/engines/pi/workspace.js +3 -1
- package/dist/pi.d.ts +2 -1
- package/dist/scaffold/add-channel.js +3 -1
- package/dist/scaffold/templates/fastagent.config.mjs +1 -0
- package/package.json +7 -4
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI kernel — commands as data. A {@link CommandSpec} carries everything the CLI surface needs
|
|
3
|
+
* (summary, args/flags, examples, narrative notes, a lazy `run`), and {@link buildProgram} renders
|
|
4
|
+
* the set through commander. Commander is called ONLY from this module; the notation specs are
|
|
5
|
+
* written in — docopt-style argument brackets (`<required>`/`[optional]`), the flag DSL
|
|
6
|
+
* (`--auth-path <file>`, `--no-x` negation), and the derived option keys ({@link optionKey}) — is a
|
|
7
|
+
* contract this module owns and validates at build time. Replacing the parser means re-implementing
|
|
8
|
+
* that notation here (one module), not editing the specs.
|
|
9
|
+
*
|
|
10
|
+
* Follows clig.dev: per-command help in four spellings (`-h`/`--help`/`help <cmd>`/bare-with-missing-args),
|
|
11
|
+
* examples in help, "did you mean" suggestions (never auto-run), and one exit-code policy — 0 success,
|
|
12
|
+
* 1 runtime failure (owned by the command bodies), 2 usage error (anything the parser itself rejects).
|
|
13
|
+
*/
|
|
14
|
+
import { Argument, Command, Help, InvalidArgumentError, Option } from "commander";
|
|
15
|
+
import { errorPrefix } from "./fail.js";
|
|
16
|
+
/**
|
|
17
|
+
* The option key a flag string yields on the parsed-flags record — THE naming rule specs rely on:
|
|
18
|
+
* camelCase of the long name (`--auth-path` → `authPath`); a `--no-x` flag negates and stores under
|
|
19
|
+
* `x` (absent ⇒ `x !== false`). Owned and enforced here so `conflicts` references and run-body reads
|
|
20
|
+
* answer to one authority, not to an implicit parser behavior. Throws on a flag without a long form
|
|
21
|
+
* (clig: every flag has a full-length spelling).
|
|
22
|
+
*/
|
|
23
|
+
export function optionKey(flags) {
|
|
24
|
+
const long = flags
|
|
25
|
+
.split(/[\s,|]+/)
|
|
26
|
+
.filter((part) => part.startsWith("--"))
|
|
27
|
+
.at(-1);
|
|
28
|
+
if (!long)
|
|
29
|
+
throw new Error(`flag "${flags}" has no long form (clig: have full-length flags)`);
|
|
30
|
+
let name = long.replace(/^--/, "").replace(/[=<[].*$/, "");
|
|
31
|
+
if (name.startsWith("no-"))
|
|
32
|
+
name = name.slice(3);
|
|
33
|
+
return name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
34
|
+
}
|
|
35
|
+
// Help styling (clig: formatting with intention): section headings are BOLD, nothing in help is
|
|
36
|
+
// colored — the only color in the whole CLI is the red error prefix. The style is always embedded;
|
|
37
|
+
// commander strips every SGR code from the assembled help whenever the target stream has no colors
|
|
38
|
+
// (non-TTY pipe, NO_COLOR, TERM=dumb).
|
|
39
|
+
const title = (s) => `\x1b[1m${s}\x1b[0m`; // bold — section headings
|
|
40
|
+
/** The spec behind each registered command — how the Help renderer reaches Examples/notes. */
|
|
41
|
+
const specOf = new WeakMap();
|
|
42
|
+
/** Build the commander program for `specs`. The CLI entry parses with it; tests inject the IO seams. */
|
|
43
|
+
export function buildProgram(specs, options = {}) {
|
|
44
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
45
|
+
const program = new Command("fastagent");
|
|
46
|
+
program.description("Serve a file-defined agent — persona.md, skills/, tools/, channels/ — as a live service.");
|
|
47
|
+
// The exit-code policy: commander throws only for parse-level events — help/version displays carry
|
|
48
|
+
// exitCode 0 (→ 0), everything else it rejects is a usage error (→ 2). Runtime failures never pass
|
|
49
|
+
// through here; command bodies exit 1 themselves.
|
|
50
|
+
program.exitOverride((err) => exit(err.exitCode === 0 ? 0 : 2));
|
|
51
|
+
program.configureHelp({
|
|
52
|
+
...(options.helpWidth !== undefined ? { helpWidth: options.helpWidth } : {}),
|
|
53
|
+
styleTitle: title,
|
|
54
|
+
// ONE renderer for the whole page: commander's standard sections, then our Examples/notes —
|
|
55
|
+
// rendered with the SAME helper (helpWidth, styleTitle), so custom sections wrap and style
|
|
56
|
+
// exactly like native ones on any terminal.
|
|
57
|
+
formatHelp: (cmd, helper) => Help.prototype.formatHelp.call(helper, cmd, helper) + extraSections(cmd, helper),
|
|
58
|
+
});
|
|
59
|
+
program.configureOutput({
|
|
60
|
+
...(options.out ? { writeOut: options.out } : {}),
|
|
61
|
+
...(options.err ? { writeErr: options.err } : {}),
|
|
62
|
+
...(options.colors !== undefined
|
|
63
|
+
? { getOutHasColors: () => options.colors, getErrHasColors: () => options.colors }
|
|
64
|
+
: {}),
|
|
65
|
+
// Every parse-level error carries the ONE unified prefix: bold-red `Error:` (plain when stderr
|
|
66
|
+
// has no colors). Command bodies get the same prefix through failStartup/failUsage.
|
|
67
|
+
outputError: (str, write) => write(str.startsWith("error:") ? `${errorPrefix(options.colors)}${str.slice("error:".length)}` : str),
|
|
68
|
+
});
|
|
69
|
+
program.showSuggestionAfterError(); // "did you mean models?" — suggest only, never run it (clig on DWIM)
|
|
70
|
+
program.showHelpAfterError("(run with --help for usage)");
|
|
71
|
+
if (options.version)
|
|
72
|
+
program.version(options.version, "-v, --version", "print the fastagent version");
|
|
73
|
+
if (options.examples || options.notes) {
|
|
74
|
+
specOf.set(program, { name: "fastagent", summary: "", examples: options.examples, notes: options.notes });
|
|
75
|
+
}
|
|
76
|
+
// Subcommands inherit exitOverride/output/suggestion settings at .command() time — register last.
|
|
77
|
+
for (const spec of specs)
|
|
78
|
+
register(program, spec);
|
|
79
|
+
return program;
|
|
80
|
+
}
|
|
81
|
+
function register(parent, spec) {
|
|
82
|
+
// Validate the spec's option references BEFORE handing anything to commander: every flag must
|
|
83
|
+
// have a long form (optionKey throws), and conflicts must name keys that exist on THIS command —
|
|
84
|
+
// commander matches conflicts by name at parse time, so a typo would otherwise silently never fire.
|
|
85
|
+
const keys = new Set((spec.flags ?? []).map((f) => optionKey(f.flags)));
|
|
86
|
+
for (const f of spec.flags ?? []) {
|
|
87
|
+
for (const target of f.conflicts ?? []) {
|
|
88
|
+
if (!keys.has(target)) {
|
|
89
|
+
throw new Error(`command "${spec.name}": "${f.flags}" conflicts with unknown option key "${target}"`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const cmd = parent.command(spec.name);
|
|
94
|
+
specOf.set(cmd, spec);
|
|
95
|
+
cmd.summary(spec.summary);
|
|
96
|
+
cmd.description(spec.description ?? spec.summary);
|
|
97
|
+
for (const a of spec.args ?? []) {
|
|
98
|
+
const arg = new Argument(a.name, a.description);
|
|
99
|
+
if (a.default !== undefined)
|
|
100
|
+
arg.default(a.default);
|
|
101
|
+
if (a.choices)
|
|
102
|
+
arg.choices(a.choices);
|
|
103
|
+
// A required argument means a non-empty VALUE, not just a present token: `invoke ""` must be a
|
|
104
|
+
// usage error, not an empty turn (the old dispatch's falsy guards, kept at the parse boundary).
|
|
105
|
+
// choices args validate membership already ("" is never a member).
|
|
106
|
+
if (a.name.startsWith("<") && !a.choices) {
|
|
107
|
+
arg.argParser((value) => {
|
|
108
|
+
if (value.trim() === "")
|
|
109
|
+
throw new InvalidArgumentError("must not be empty.");
|
|
110
|
+
return value;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
cmd.addArgument(arg);
|
|
114
|
+
}
|
|
115
|
+
for (const f of spec.flags ?? []) {
|
|
116
|
+
const opt = new Option(f.flags, f.description);
|
|
117
|
+
if (f.hidden)
|
|
118
|
+
opt.hideHelp();
|
|
119
|
+
if (f.conflicts)
|
|
120
|
+
opt.conflicts(f.conflicts);
|
|
121
|
+
cmd.addOption(opt);
|
|
122
|
+
}
|
|
123
|
+
for (const sub of spec.subcommands ?? [])
|
|
124
|
+
register(cmd, sub);
|
|
125
|
+
const run = spec.run;
|
|
126
|
+
if (run) {
|
|
127
|
+
cmd.action(async (...invocation) => {
|
|
128
|
+
invocation.pop(); // the Command instance
|
|
129
|
+
const flags = invocation.pop();
|
|
130
|
+
await run(invocation, flags);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The Examples/notes sections a spec appends after commander's standard ones. Examples are
|
|
136
|
+
* preformatted (column-aligned `$ cmd # note` lines); notes are logical prose the renderer
|
|
137
|
+
* reflows to `helper.helpWidth` — the exact width the sections above were wrapped to.
|
|
138
|
+
*/
|
|
139
|
+
function extraSections(cmd, helper) {
|
|
140
|
+
const spec = specOf.get(cmd);
|
|
141
|
+
if (!spec || ((spec.examples?.length ?? 0) === 0 && !spec.notes))
|
|
142
|
+
return "";
|
|
143
|
+
const width = helper.helpWidth ?? 80;
|
|
144
|
+
const lines = [];
|
|
145
|
+
if (spec.examples && spec.examples.length > 0) {
|
|
146
|
+
lines.push(helper.styleTitle("Examples:"));
|
|
147
|
+
// Inline, column-aligned notes (`$ cmd # note`) — a note on its own line reads as a stray
|
|
148
|
+
// fragment when neighboring examples have none.
|
|
149
|
+
const w = Math.max(...spec.examples.map((e) => e.cmd.length));
|
|
150
|
+
for (const e of spec.examples) {
|
|
151
|
+
lines.push(e.note ? ` $ ${e.cmd.padEnd(w)} # ${e.note}` : ` $ ${e.cmd}`);
|
|
152
|
+
}
|
|
153
|
+
lines.push("");
|
|
154
|
+
}
|
|
155
|
+
if (spec.notes) {
|
|
156
|
+
lines.push(...reflow(spec.notes, width, helper));
|
|
157
|
+
lines.push("");
|
|
158
|
+
}
|
|
159
|
+
return `\n${lines.join("\n")}`;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Reflow notes to the help width: plain lines are prose (joined and wrapped via the helper's own
|
|
163
|
+
* `boxWrap`); indented lines are preformatted (aligned tables like start's precedence chains) and
|
|
164
|
+
* pass through verbatim; blank lines separate paragraphs.
|
|
165
|
+
*/
|
|
166
|
+
function reflow(text, width, helper) {
|
|
167
|
+
const out = [];
|
|
168
|
+
let prose = [];
|
|
169
|
+
const flush = () => {
|
|
170
|
+
if (prose.length > 0) {
|
|
171
|
+
out.push(...helper.boxWrap(prose.join(" "), width).split("\n"));
|
|
172
|
+
prose = [];
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
for (const line of text.split("\n")) {
|
|
176
|
+
if (/^\s/.test(line)) {
|
|
177
|
+
flush();
|
|
178
|
+
out.push(line);
|
|
179
|
+
}
|
|
180
|
+
else if (line === "") {
|
|
181
|
+
flush();
|
|
182
|
+
out.push("");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
prose.push(line);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
flush();
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CommandSpec, type ProgramOptions } from "./kernel.ts";
|
|
2
|
+
/** Registration order = help order — the ORIGINAL usage wall's order, kept verbatim (this is a
|
|
3
|
+
* commander refactor of the same CLI, not a redesign). Exported for the kernel conformance tests. */
|
|
4
|
+
export declare const specs: readonly CommandSpec[];
|
|
5
|
+
/**
|
|
6
|
+
* The production program assembly (specs + the top-level examples/docs). Tests build through THIS —
|
|
7
|
+
* with their IO/width/color seams as overrides — so they exercise the real shape, not a lookalike.
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildCliProgram(overrides?: ProgramOptions): import("commander").Command;
|
|
10
|
+
/** Parse and run one CLI invocation (`argv` = process.argv). Usage errors exit 2 via the kernel policy. */
|
|
11
|
+
export declare function runCli(argv: readonly string[]): Promise<void>;
|
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fastagent command registry: every command as data ({@link CommandSpec}) with a lazy-imported
|
|
3
|
+
* implementation, so `fastagent <cmd>` pays only for the module graph that command actually uses.
|
|
4
|
+
* This file is the CLI surface's single source of truth — the overview and every per-command help
|
|
5
|
+
* render from these specs (no hand-maintained usage text).
|
|
6
|
+
*/
|
|
7
|
+
import { fastagentVersion } from "../version.js";
|
|
8
|
+
import { buildProgram } from "./kernel.js";
|
|
9
|
+
// Help groups (clig: most common commands first) — the authoring loop leads, operations close.
|
|
10
|
+
// Shared flags — same name, same meaning, on every command that supports them (clig: consistency).
|
|
11
|
+
const DIR_ARG = { name: "[dir]", description: "workspace directory", default: "." };
|
|
12
|
+
const MODEL = {
|
|
13
|
+
flags: "--model <provider/modelId>",
|
|
14
|
+
description: "model override (precedence: --model > FASTAGENT_MODEL > config)",
|
|
15
|
+
};
|
|
16
|
+
const AUTH_PATH = {
|
|
17
|
+
flags: "--auth-path <file>",
|
|
18
|
+
description: "credentials file (default: <state root>/auth.json; env: FASTAGENT_AUTH_PATH)",
|
|
19
|
+
};
|
|
20
|
+
const JSON_FLAG = { flags: "--json", description: "machine-readable JSON output" };
|
|
21
|
+
const NO_INPUT = {
|
|
22
|
+
flags: "--no-input",
|
|
23
|
+
description: "never prompt (CI/scripts) — missing information becomes an error instead of a question",
|
|
24
|
+
};
|
|
25
|
+
const PORT = { flags: "--port <n>", description: "HTTP port" };
|
|
26
|
+
const TUNNEL = {
|
|
27
|
+
flags: "--tunnel",
|
|
28
|
+
description: "expose a public HTTPS URL via a Cloudflare quick tunnel (needs cloudflared) and auto-register " +
|
|
29
|
+
"webhook channels (telegram, feishu, lark; github prints the URL) — for hosting a bot from your " +
|
|
30
|
+
"own box without deploying (the quick-tunnel URL is ephemeral, not for production)",
|
|
31
|
+
};
|
|
32
|
+
const init = {
|
|
33
|
+
name: "init",
|
|
34
|
+
summary: "scaffold a runnable agent and install its dependencies",
|
|
35
|
+
description: "Scaffold a runnable agent in dir (default .) and run npm install. Default is a self-iterating " +
|
|
36
|
+
"agent: persona.md (its identity), a writing-great-skills example skill, a fetch-url code tool, " +
|
|
37
|
+
"config, package.json, .gitignore. Never overwrites existing files; an existing AGENTS.md is kept " +
|
|
38
|
+
"as project context.",
|
|
39
|
+
args: [DIR_ARG],
|
|
40
|
+
flags: [
|
|
41
|
+
{ flags: "--minimal", description: "persona.md + the example skill + config only (no code tool / package.json)" },
|
|
42
|
+
{ flags: "--no-install", description: "scaffold everything but skip npm install" },
|
|
43
|
+
{ flags: "--flat", description: "force the flat layout (skip host-signal detection)", conflicts: ["agentDir"] },
|
|
44
|
+
{ flags: "--agent-dir <name>", description: "force the agent kit into ./<name>" },
|
|
45
|
+
],
|
|
46
|
+
examples: [
|
|
47
|
+
{ cmd: "fastagent init my-agent", note: "a new agent dir, ready to dev" },
|
|
48
|
+
{ cmd: "fastagent init", note: "initialize the current directory" },
|
|
49
|
+
],
|
|
50
|
+
notes: 'Layout: flat by default ("a directory is an agent"); when an existing toolchain/deploy claims ' +
|
|
51
|
+
"the directory (tsconfig/framework config, a non-JS build manifest like " +
|
|
52
|
+
"go.mod/pyproject.toml/Cargo.toml, Dockerfile/fly/railway, or occupied tools/, channels/, or " +
|
|
53
|
+
"skills/), the kit goes into ./agent and config.agentDir points there — the reason is printed, " +
|
|
54
|
+
"no prompt.",
|
|
55
|
+
run: async (args, f) => (await import("./commands/init.js")).runInit(args[0], {
|
|
56
|
+
minimal: f.minimal === true,
|
|
57
|
+
install: f.install !== false,
|
|
58
|
+
flat: f.flat === true,
|
|
59
|
+
agentDir: f.agentDir,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
const dev = {
|
|
63
|
+
name: "dev",
|
|
64
|
+
summary: "serve the agent locally, restarting on code edits",
|
|
65
|
+
description: "Assemble the agent in dir (default .) and serve a local HTTP channel. persona.md/AGENTS.md/skills " +
|
|
66
|
+
"are re-read every turn (edits go live next turn); edits to code inputs — tools/, channels/, " +
|
|
67
|
+
"fastagent.config.*, package.json, .env — restart the worker. Files the agent writes as work " +
|
|
68
|
+
"product never trigger a restart.",
|
|
69
|
+
args: [DIR_ARG],
|
|
70
|
+
flags: [
|
|
71
|
+
PORT,
|
|
72
|
+
MODEL,
|
|
73
|
+
AUTH_PATH,
|
|
74
|
+
{ flags: "--no-watch", description: "serve once, no file-watching" },
|
|
75
|
+
TUNNEL,
|
|
76
|
+
NO_INPUT,
|
|
77
|
+
],
|
|
78
|
+
examples: [
|
|
79
|
+
{ cmd: "fastagent dev" },
|
|
80
|
+
{ cmd: "fastagent dev --tunnel", note: "public URL + auto-registered webhooks" },
|
|
81
|
+
],
|
|
82
|
+
run: async (args, f) => (await import("./commands/dev.js")).runDev(args[0], {
|
|
83
|
+
port: f.port,
|
|
84
|
+
model: f.model,
|
|
85
|
+
authPath: f.authPath,
|
|
86
|
+
watch: f.watch !== false,
|
|
87
|
+
tunnel: f.tunnel === true,
|
|
88
|
+
input: f.input !== false,
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
const chat = {
|
|
92
|
+
name: "chat",
|
|
93
|
+
summary: "open the SAME assembled agent in pi's interactive TUI",
|
|
94
|
+
description: "Open the SAME assembled agent in pi's interactive TUI (the real harness, not a crude REPL) — to " +
|
|
95
|
+
"try it locally before serving. Same model/tool/skill resolution as dev; pi handles login, " +
|
|
96
|
+
"sessions, and /resume natively.",
|
|
97
|
+
args: [DIR_ARG],
|
|
98
|
+
flags: [MODEL],
|
|
99
|
+
examples: [{ cmd: "fastagent chat" }],
|
|
100
|
+
run: async (args, f) => (await import("./commands/chat.js")).runChat(args[0], { model: f.model }),
|
|
101
|
+
};
|
|
102
|
+
const info = {
|
|
103
|
+
name: "info",
|
|
104
|
+
summary: "print what the directory assembles into, without serving",
|
|
105
|
+
description: "Print what dir (default .) ASSEMBLES into — model, persona, context files (AGENTS.md), skills, " +
|
|
106
|
+
"tools (+ collisions), channels, schedules, sessions, load diagnostics — WITHOUT serving. " +
|
|
107
|
+
"Read-only (never creates sessions / writes .gitignore); an unset model is reported, not fatal. " +
|
|
108
|
+
"Run it first when something looks off.",
|
|
109
|
+
args: [DIR_ARG],
|
|
110
|
+
flags: [JSON_FLAG, MODEL, AUTH_PATH, { flags: "--sessions-dir <dir>", description: "sessions directory override" }],
|
|
111
|
+
examples: [{ cmd: "fastagent info" }, { cmd: "fastagent info --json", note: "for CI" }],
|
|
112
|
+
run: async (args, f) => (await import("./commands/info.js")).runInfo(args[0], {
|
|
113
|
+
json: f.json === true,
|
|
114
|
+
model: f.model,
|
|
115
|
+
authPath: f.authPath,
|
|
116
|
+
sessionsDir: f.sessionsDir,
|
|
117
|
+
}),
|
|
118
|
+
};
|
|
119
|
+
const tool = {
|
|
120
|
+
name: "tool",
|
|
121
|
+
summary: "run one tool directly with JSON args — no model, no server, no tokens",
|
|
122
|
+
description: "Run one tool (from tools/ or config.tools) directly with JSON args — no model, no server, no " +
|
|
123
|
+
"tokens. Fast feedback while authoring a tool.",
|
|
124
|
+
args: [
|
|
125
|
+
{ name: "<name>", description: "the tool name as served (see `fastagent info`)" },
|
|
126
|
+
{ name: "[json-args]", description: "the tool's arguments as a JSON object", default: "{}" },
|
|
127
|
+
DIR_ARG,
|
|
128
|
+
],
|
|
129
|
+
examples: [{ cmd: `fastagent tool add '{"a":2,"b":3}'` }],
|
|
130
|
+
notes: "Mounts the same tool set dev/start serve (defaults + config.tools + discovered tools/, " +
|
|
131
|
+
"deduped), so a shadowed or broken tool is surfaced here exactly as it would be when serving.",
|
|
132
|
+
run: async (args) => (await import("./commands/tool.js")).runTool(args[0], args[1], args[2]),
|
|
133
|
+
};
|
|
134
|
+
const invoke = {
|
|
135
|
+
name: "invoke",
|
|
136
|
+
summary: "run ONE turn against the assembled agent and exit",
|
|
137
|
+
description: "Run ONE turn against the assembled agent and exit — no server, no TUI. The reply streams to " +
|
|
138
|
+
"stdout, tool/diagnostics to stderr, a failed turn exits non-zero. The all-agent counterpart of " +
|
|
139
|
+
"`tool`, for CI smoke and quick checks. Same model resolution as dev.",
|
|
140
|
+
args: [{ name: "<message>", description: "the user message for the turn" }, DIR_ARG],
|
|
141
|
+
flags: [MODEL, AUTH_PATH, NO_INPUT],
|
|
142
|
+
examples: [{ cmd: `fastagent invoke "summarize today's inbox"` }],
|
|
143
|
+
run: async (args, f) => (await import("./commands/invoke.js")).runInvoke(args[0], args[1], {
|
|
144
|
+
model: f.model,
|
|
145
|
+
authPath: f.authPath,
|
|
146
|
+
input: f.input !== false,
|
|
147
|
+
}),
|
|
148
|
+
};
|
|
149
|
+
const fire = {
|
|
150
|
+
name: "fire",
|
|
151
|
+
summary: "run ONE schedule's turn immediately, without waiting for its cron",
|
|
152
|
+
description: "Run ONE schedule's turn immediately (authoring loop, like invoke) — fires schedules/<name>.ts now " +
|
|
153
|
+
"without waiting for its cron. Reply→stdout; does NOT advance the schedule's fire state.",
|
|
154
|
+
args: [{ name: "<name>", description: "the schedule name (schedules/<name>.ts)" }, DIR_ARG],
|
|
155
|
+
flags: [MODEL, AUTH_PATH, NO_INPUT],
|
|
156
|
+
examples: [{ cmd: "fastagent fire daily-digest" }],
|
|
157
|
+
run: async (args, f) => (await import("./commands/fire.js")).runFire(args[0], args[1], {
|
|
158
|
+
model: f.model,
|
|
159
|
+
authPath: f.authPath,
|
|
160
|
+
input: f.input !== false,
|
|
161
|
+
}),
|
|
162
|
+
};
|
|
163
|
+
const models = {
|
|
164
|
+
name: "models",
|
|
165
|
+
summary: 'list the available "provider/modelId" model specs',
|
|
166
|
+
description: 'List every registered "provider/modelId" spec — use one with --model or as `model` in fastagent.config.ts.',
|
|
167
|
+
args: [{ name: "[search]", description: "case-insensitive substring filter" }],
|
|
168
|
+
examples: [
|
|
169
|
+
{ cmd: "fastagent models", note: "all specs" },
|
|
170
|
+
{ cmd: "fastagent models claude", note: "filter; provider-name matches rank first" },
|
|
171
|
+
],
|
|
172
|
+
run: async (args) => (await import("./commands/models.js")).runModels(args[0]),
|
|
173
|
+
};
|
|
174
|
+
const start = {
|
|
175
|
+
name: "start",
|
|
176
|
+
summary: "run the agent in production posture (same assembly as dev, no watching)",
|
|
177
|
+
description: "Run the agent in dir (default .) in production posture — the SAME assembly as dev (your directory " +
|
|
178
|
+
"is the agent), just no file-watching. No build step: start reads the definition directly; " +
|
|
179
|
+
"model/http come from fastagent.config.ts (frozen by git).",
|
|
180
|
+
args: [DIR_ARG],
|
|
181
|
+
flags: [
|
|
182
|
+
PORT,
|
|
183
|
+
MODEL,
|
|
184
|
+
{ flags: "--sessions-dir <dir>", description: "sessions directory override" },
|
|
185
|
+
AUTH_PATH,
|
|
186
|
+
TUNNEL,
|
|
187
|
+
NO_INPUT,
|
|
188
|
+
],
|
|
189
|
+
examples: [
|
|
190
|
+
{ cmd: "fastagent start" },
|
|
191
|
+
{ cmd: "fastagent start --tunnel", note: "host a bot from your own box, no deploy" },
|
|
192
|
+
],
|
|
193
|
+
notes: "Precedence chains:\n" +
|
|
194
|
+
" port: --port > PORT env > fastagent.config.ts http.port > 8787\n" +
|
|
195
|
+
" state: FASTAGENT_STATE_DIR > <dir>/.fastagent — the ONE machine-state\n" +
|
|
196
|
+
" root (auth, sessions, channel state all derive from it); point\n" +
|
|
197
|
+
" it at a mounted volume so a redeploy that replaces the\n" +
|
|
198
|
+
" directory never wipes state\n" +
|
|
199
|
+
" sessions: --sessions-dir > FASTAGENT_SESSIONS_DIR > <state>/sessions\n" +
|
|
200
|
+
" auth: --auth-path > FASTAGENT_AUTH_PATH > <state>/auth.json\n" +
|
|
201
|
+
" (project-level; point it at ~/.fastagent/auth.json to share one\n" +
|
|
202
|
+
" credential across projects)",
|
|
203
|
+
run: async (args, f) => (await import("./commands/start.js")).runStart(args[0], {
|
|
204
|
+
port: f.port,
|
|
205
|
+
model: f.model,
|
|
206
|
+
sessionsDir: f.sessionsDir,
|
|
207
|
+
authPath: f.authPath,
|
|
208
|
+
tunnel: f.tunnel === true,
|
|
209
|
+
input: f.input !== false,
|
|
210
|
+
}),
|
|
211
|
+
};
|
|
212
|
+
/** The retired app-creation flag — parsed so it can explain itself, hidden from help. */
|
|
213
|
+
const CREATE_APP = { flags: "--create-app", description: "(retired)", hidden: true };
|
|
214
|
+
const channelSub = (kind, summary, description, notes) => ({
|
|
215
|
+
name: kind,
|
|
216
|
+
summary,
|
|
217
|
+
description,
|
|
218
|
+
args: [DIR_ARG],
|
|
219
|
+
flags: [CREATE_APP],
|
|
220
|
+
examples: [{ cmd: `fastagent add ${kind}` }],
|
|
221
|
+
...(notes ? { notes } : {}),
|
|
222
|
+
run: async (args, f) => (await import("./commands/add.js")).runAddChannel(kind, args[0], { createApp: f.createApp === true }),
|
|
223
|
+
});
|
|
224
|
+
const add = {
|
|
225
|
+
name: "add",
|
|
226
|
+
summary: "connect a channel (github, telegram, feishu, lark) or vendor a skill",
|
|
227
|
+
description: "Scaffold channels/<kind>.ts — third-party adapter glue with the policy to edit (github maps " +
|
|
228
|
+
"events in on(); telegram/feishu/lark route in the optional route()) — or vendor an Agent Skills " +
|
|
229
|
+
"skill into skills/<name>/.",
|
|
230
|
+
subcommands: [
|
|
231
|
+
channelSub("github", "scaffold the GitHub webhook channel (issues/PRs → agent turns)", "Scaffold channels/github.ts — webhook adapter glue that maps repository events (issues, PRs, " +
|
|
232
|
+
"comments) to agent turns in its on() policy."),
|
|
233
|
+
channelSub("telegram", "scaffold the Telegram bot channel (durable turns, live preview)", "Scaffold channels/telegram.ts — the Telegram bot channel with durable turns, a live-preview " +
|
|
234
|
+
"message pump, and an optional route() policy."),
|
|
235
|
+
channelSub("feishu", "scaffold the Feishu channel AND create/configure the platform app", "Scaffold channels/feishu.ts AND create/configure the Feishu platform app (scan-to-create), " +
|
|
236
|
+
"writing credentials to .env.", "Feishu (open.feishu.cn) is the canonical implementation. `add feishu` also CREATES + " +
|
|
237
|
+
'configures the platform app (confirm a link in the app — the platform\'s "scan to create" ' +
|
|
238
|
+
"flow; one version-publish action remains) and writes credentials to .env; a persisted " +
|
|
239
|
+
"ID/Secret pair resumes missing-Token setup instead of creating another app."),
|
|
240
|
+
channelSub("lark", "scaffold the Lark (international) channel with guided credential setup", "Scaffold channels/lark.ts — the Lark international profile over the Feishu engine — and guide " +
|
|
241
|
+
"credential setup against the intl developer console.", "Lark international (open.larksuite.com) is Feishu's compatibility profile with degraded " +
|
|
242
|
+
"control-plane setup: opens the intl developer console only for a new/partial pair, validates " +
|
|
243
|
+
"App ID/Secret, then probes webhook-mode + Token automation; an explicit config-route 404 " +
|
|
244
|
+
"falls back to a hidden Token prompt + manual mode/URL setup."),
|
|
245
|
+
{
|
|
246
|
+
name: "skill",
|
|
247
|
+
summary: "vendor an Agent Skills skill into skills/<name>/ (copied in, git-tracked)",
|
|
248
|
+
args: [
|
|
249
|
+
{
|
|
250
|
+
name: "[source]",
|
|
251
|
+
description: "a git ref (owner/repo/path, github default), a local path (./x, /abs), or a bare name " +
|
|
252
|
+
"from your global skill dirs (~/.agents/skills, ~/.pi/agent/skills)",
|
|
253
|
+
},
|
|
254
|
+
DIR_ARG,
|
|
255
|
+
],
|
|
256
|
+
flags: [
|
|
257
|
+
{ flags: "--update", description: "overwrite an existing skill (re-fetch from source); review with git diff" },
|
|
258
|
+
],
|
|
259
|
+
examples: [
|
|
260
|
+
{ cmd: "fastagent add skill anthropics/skills/document-skills/pdf" },
|
|
261
|
+
{ cmd: "fastagent add skill ./my-skill --update" },
|
|
262
|
+
],
|
|
263
|
+
notes: "Writing your own skill needs no command: create skills/<name>/SKILL.md with name + " +
|
|
264
|
+
"description frontmatter; it's auto-discovered. `add skill` is only for vendoring an " +
|
|
265
|
+
"existing one.",
|
|
266
|
+
run: async (args, f) => (await import("./commands/add.js")).runAddSkill(args[0], args[1], { update: f.update === true }),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
const deploy = {
|
|
271
|
+
name: "deploy",
|
|
272
|
+
summary: "generate deploy artifacts + a runbook for docker, fly, or railway (--run drives it)",
|
|
273
|
+
description: "Generate Dockerfile/.dockerignore plus the target config and print an ordered runbook. " +
|
|
274
|
+
"docker: fastagent.compose.yml, loopback port, persistent state volume. fly: fly.toml " +
|
|
275
|
+
"(autostop=suspend, state→volume). railway: railway.json (healthcheck /health); its " +
|
|
276
|
+
"volume/variables/App-Sleeping are dashboard/CLI steps the runbook states. Durable ingress " +
|
|
277
|
+
"remains operator-owned.",
|
|
278
|
+
args: [{ name: "<host>", description: "deploy target", choices: ["docker", "fly", "railway"] }, DIR_ARG],
|
|
279
|
+
flags: [
|
|
280
|
+
{
|
|
281
|
+
flags: "--run",
|
|
282
|
+
description: "drive the target CLI to completion. Docker runs `docker compose up -d --build`; with a tunnel " +
|
|
283
|
+
"service, reads its URL and registers webhooks. Fly/Railway provision app/service + volume + " +
|
|
284
|
+
"secrets + deploy + webhook setup. Carries your local credential (env key or OAuth auth.json). " +
|
|
285
|
+
"Stops at a gate (missing CLI/daemon/login/secret) with one actionable line. Without it: prints " +
|
|
286
|
+
"the runbook",
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
flags: "--tunnel",
|
|
290
|
+
description: "(docker only) add a Quick Tunnel service to generated Compose; generation-only unless combined " +
|
|
291
|
+
"with --run. Existing Compose stays authoritative",
|
|
292
|
+
},
|
|
293
|
+
{ flags: "--force", description: "overwrite existing target config/Dockerfile/.dockerignore (else kept)" },
|
|
294
|
+
{
|
|
295
|
+
flags: "--stop",
|
|
296
|
+
description: "(fly only) autostop by stopping (cold start) instead of suspending (fast resume)",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
flags: "--no-scale-to-zero",
|
|
300
|
+
description: "(fly only) keep one machine running when idle (min_machines_running=1)",
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
flags: "--into-linked",
|
|
304
|
+
description: "(railway --run) provision INTO the project this dir is already linked to (skip create); by " +
|
|
305
|
+
"default --run refuses a pre-existing link (could be unrelated/production)",
|
|
306
|
+
},
|
|
307
|
+
MODEL,
|
|
308
|
+
AUTH_PATH,
|
|
309
|
+
],
|
|
310
|
+
examples: [
|
|
311
|
+
{ cmd: "fastagent deploy fly --run", note: "provision + deploy + webhooks" },
|
|
312
|
+
{ cmd: "fastagent deploy docker --tunnel --run", note: "Compose + a public URL" },
|
|
313
|
+
{ cmd: "fastagent deploy railway", note: "print the runbook only" },
|
|
314
|
+
],
|
|
315
|
+
notes: "Definition-read-only: the only writes are generated artifacts (never clobbered without " +
|
|
316
|
+
"--force). A routine redeploy of an already-provisioned agent is just the host's own command " +
|
|
317
|
+
"(e.g. `railway up`).",
|
|
318
|
+
run: async (args, f) => (await import("./commands/deploy.js")).runDeploy(args[0], args[1], {
|
|
319
|
+
run: f.run === true,
|
|
320
|
+
tunnel: f.tunnel === true,
|
|
321
|
+
force: f.force === true,
|
|
322
|
+
stop: f.stop === true,
|
|
323
|
+
scaleToZero: f.scaleToZero !== false,
|
|
324
|
+
intoLinked: f.intoLinked === true,
|
|
325
|
+
model: f.model,
|
|
326
|
+
authPath: f.authPath,
|
|
327
|
+
}),
|
|
328
|
+
};
|
|
329
|
+
const schedule = {
|
|
330
|
+
name: "schedule",
|
|
331
|
+
summary: "inspect and control time triggers: run audit, pending fires, cancel a wake-up",
|
|
332
|
+
subcommands: [
|
|
333
|
+
{
|
|
334
|
+
name: "history",
|
|
335
|
+
summary: 'print the run audit for a schedule (or "wake" for self-scheduled wake-ups)',
|
|
336
|
+
description: "Print the run audit for a schedule: when each run fired, completed/failed/deferred, duration, and " +
|
|
337
|
+
'the reply/error — the answer to "did last night\'s run silently fail?". Read-only.',
|
|
338
|
+
args: [
|
|
339
|
+
{ name: "<name>", description: 'the schedule name, or "wake" for the agent\'s self-scheduled wake-ups' },
|
|
340
|
+
DIR_ARG,
|
|
341
|
+
],
|
|
342
|
+
flags: [{ flags: "--json", description: "the full records (complete reply text)" }],
|
|
343
|
+
examples: [
|
|
344
|
+
{ cmd: "fastagent schedule history daily-digest" },
|
|
345
|
+
{ cmd: "fastagent schedule history wake --json", note: "the agent's own wake-ups" },
|
|
346
|
+
],
|
|
347
|
+
run: async (args, flags) => (await import("./commands/schedule.js")).runScheduleHistory(args[0], args[1], flags.json === true),
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
name: "list",
|
|
351
|
+
summary: "everything that will fire: static schedules (next instant) + pending wake-ups",
|
|
352
|
+
description: "List everything that will fire, from BOTH producers: the static schedules/ files (name + next " +
|
|
353
|
+
"cron instant) and the agent's pending self-scheduled wake-ups. Read-only.",
|
|
354
|
+
args: [DIR_ARG],
|
|
355
|
+
flags: [JSON_FLAG],
|
|
356
|
+
examples: [{ cmd: "fastagent schedule list" }],
|
|
357
|
+
run: async (args, flags) => (await import("./commands/schedule.js")).runScheduleList(args[0], flags.json === true),
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
name: "cancel",
|
|
361
|
+
summary: "remove a pending wake-up — the operator's kill switch for a runaway recurring wake",
|
|
362
|
+
description: "Remove a pending wake-up — the operator's kill switch for a runaway recurring wake (the agent's own " +
|
|
363
|
+
"is the `unwake` tool). Not session-scoped: the operator owns the box.",
|
|
364
|
+
args: [{ name: "<id>", description: "the wake-up id (`fastagent schedule list` shows ids)" }, DIR_ARG],
|
|
365
|
+
examples: [{ cmd: "fastagent schedule cancel wake-1700000000000-ab12" }],
|
|
366
|
+
run: async (args) => (await import("./commands/schedule.js")).runScheduleCancel(args[0], args[1]),
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
};
|
|
370
|
+
const login = {
|
|
371
|
+
name: "login",
|
|
372
|
+
summary: "authenticate a model provider (subscription/OAuth or API key)",
|
|
373
|
+
description: "Authenticate a model provider into the project-level <state root>/auth.json — default " +
|
|
374
|
+
"<cwd>/.fastagent/auth.json (run from $HOME for the global ~/.fastagent/auth.json): pick a method " +
|
|
375
|
+
"(subscription/OAuth or API key), then a provider that offers it (configured status shown). " +
|
|
376
|
+
"[provider] takes the method from what that provider supports, asked only when both.",
|
|
377
|
+
args: [{ name: "[provider]", description: "provider id (skip the provider menu)" }],
|
|
378
|
+
flags: [AUTH_PATH, NO_INPUT],
|
|
379
|
+
examples: [{ cmd: "fastagent login" }, { cmd: "fastagent login openai" }],
|
|
380
|
+
notes: "The positional is the PROVIDER (not a dir) — `cd` into your agent before logging in.",
|
|
381
|
+
run: async (args, f) => (await import("./commands/login.js")).runLogin(args[0], {
|
|
382
|
+
authPath: f.authPath,
|
|
383
|
+
input: f.input !== false,
|
|
384
|
+
}),
|
|
385
|
+
};
|
|
386
|
+
/** Registration order = help order — the ORIGINAL usage wall's order, kept verbatim (this is a
|
|
387
|
+
* commander refactor of the same CLI, not a redesign). Exported for the kernel conformance tests. */
|
|
388
|
+
export const specs = [
|
|
389
|
+
init,
|
|
390
|
+
models,
|
|
391
|
+
info,
|
|
392
|
+
tool,
|
|
393
|
+
invoke,
|
|
394
|
+
fire,
|
|
395
|
+
schedule,
|
|
396
|
+
dev,
|
|
397
|
+
chat,
|
|
398
|
+
start,
|
|
399
|
+
add,
|
|
400
|
+
deploy,
|
|
401
|
+
login,
|
|
402
|
+
];
|
|
403
|
+
/**
|
|
404
|
+
* The production program assembly (specs + the top-level examples/docs). Tests build through THIS —
|
|
405
|
+
* with their IO/width/color seams as overrides — so they exercise the real shape, not a lookalike.
|
|
406
|
+
*/
|
|
407
|
+
export function buildCliProgram(overrides = {}) {
|
|
408
|
+
return buildProgram(specs, {
|
|
409
|
+
examples: [
|
|
410
|
+
{ cmd: "fastagent init my-agent && cd my-agent", note: "scaffold an agent" },
|
|
411
|
+
{ cmd: "fastagent dev", note: "serve locally and iterate" },
|
|
412
|
+
{ cmd: "fastagent deploy fly --run", note: "ship it" },
|
|
413
|
+
],
|
|
414
|
+
notes: "Docs: https://github.com/fastagent-sh/fastagent",
|
|
415
|
+
...overrides,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/** Parse and run one CLI invocation (`argv` = process.argv). Usage errors exit 2 via the kernel policy. */
|
|
419
|
+
export async function runCli(argv) {
|
|
420
|
+
await buildCliProgram({ version: await fastagentVersion() }).parseAsync([...argv]);
|
|
421
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The serving spine shared by `dev` (its worker) and `start`: route assembly from discovered
|
|
3
|
+
* channels/, the Node host binding, the scheduler lifecycle, and the optional Cloudflare quick
|
|
4
|
+
* tunnel. Bodies moved verbatim from cli.ts; `values.tunnel` became a parameter.
|
|
5
|
+
*/
|
|
6
|
+
import type { Agent } from "../agent.ts";
|
|
7
|
+
import { type Routes } from "../host/node.ts";
|
|
8
|
+
/**
|
|
9
|
+
* The routes this deployment serves: a default `GET /health` plus the workspace's discovered
|
|
10
|
+
* `channels/` — or the default invoke channel at POST /invoke when none are declared.
|
|
11
|
+
*/
|
|
12
|
+
export declare function routesFor(workspaceDir: string, agent: Agent, stateRoot: string): Promise<Routes>;
|
|
13
|
+
/** Serve `routes` via the Node host. serveNode owns binding; the CLI owns policy (errors, ready signal, log). */
|
|
14
|
+
export declare function serve(routes: Routes, port: number, onListening?: (boundPort: number) => void): void;
|
|
15
|
+
/**
|
|
16
|
+
* Start a Cloudflare tunnel + announce/register webhooks once the server is bound — unless this is a
|
|
17
|
+
* watch-supervisor worker, where the supervisor owns the long-lived tunnel so the public URL survives
|
|
18
|
+
* reloads.
|
|
19
|
+
*/
|
|
20
|
+
export declare function maybeTunnel(workspaceDir: string, boundPort: number, tunnel: boolean): void;
|
|
21
|
+
/**
|
|
22
|
+
* Load and start the workspace's `schedules/` — a time-trigger firing the agent on each cron. Starts iff
|
|
23
|
+
* there are static schedules OR `selfSchedule` is on (the scheduler also polls the agent's self-scheduled
|
|
24
|
+
* wake-ups, which the built-in `wake` tool creates only when opted in). Shares the SAME (trace-wrapped)
|
|
25
|
+
* agent the routes serve, so a scheduled turn is observed like any other. Best-effort stop on exit; dev's
|
|
26
|
+
* watch restart re-reads schedules with the worker (schedules are a code input). Single-process.
|
|
27
|
+
*/
|
|
28
|
+
export declare function startSchedules(workspaceDir: string, agent: Agent, stateRoot: string, selfSchedule: boolean): Promise<void>;
|