@fastagent-sh/fastagent 0.12.1 → 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 +6 -4
- package/dist/channels/feishu/bootstrap-token.d.ts +42 -0
- package/dist/channels/feishu/bootstrap-token.js +94 -0
- package/dist/channels/feishu/card.d.ts +32 -0
- package/dist/channels/feishu/card.js +66 -0
- package/dist/channels/feishu/cloud.d.ts +17 -0
- package/dist/channels/feishu/cloud.js +19 -0
- 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 +15 -0
- package/dist/channels/feishu/crypto.js +43 -0
- package/dist/channels/feishu/feishu-api.d.ts +108 -0
- package/dist/channels/feishu/feishu-api.js +325 -0
- package/dist/channels/feishu/feishu.d.ts +46 -0
- package/dist/channels/feishu/feishu.js +472 -0
- package/dist/channels/feishu/invoke-turn.d.ts +65 -0
- package/dist/channels/feishu/invoke-turn.js +157 -0
- 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 +43 -0
- package/dist/channels/feishu/parse.js +65 -0
- package/dist/channels/feishu/preview.d.ts +36 -0
- package/dist/channels/feishu/preview.js +387 -0
- package/dist/channels/feishu/register-app.d.ts +70 -0
- package/dist/channels/feishu/register-app.js +141 -0
- package/dist/channels/feishu/register-webhook.d.ts +22 -0
- package/dist/channels/feishu/register-webhook.js +106 -0
- package/dist/channels/feishu/scaffold/channel.ts +41 -0
- package/dist/channels/feishu/scaffold/feishu-send.ts +87 -0
- package/dist/channels/feishu/seen.d.ts +5 -0
- package/dist/channels/feishu/seen.js +47 -0
- package/dist/channels/feishu/text.d.ts +13 -0
- package/dist/channels/feishu/text.js +63 -0
- package/dist/channels/lark/lark.d.ts +15 -0
- package/dist/channels/lark/lark.js +10 -0
- package/dist/channels/lark/onboard.d.ts +39 -0
- package/dist/channels/lark/onboard.js +58 -0
- package/dist/channels/lark/scaffold/channel.ts +39 -0
- package/dist/channels/lark/scaffold/lark-send.ts +87 -0
- package/dist/channels/registration.d.ts +15 -0
- package/dist/channels/registration.js +1 -0
- package/dist/channels/{telegram/state.js → state.js} +6 -4
- package/dist/channels/telegram/context-buffer.js +1 -1
- package/dist/channels/telegram/register-webhook.d.ts +4 -1
- package/dist/channels/telegram/register-webhook.js +17 -26
- package/dist/channels/telegram/telegram.js +2 -2
- package/dist/channels/telegram/turn-store.d.ts +8 -21
- package/dist/channels/telegram/turn-store.js +11 -130
- package/dist/channels/{telegram/turn-queue.js → turn-queue.js} +3 -3
- package/dist/channels/turn-store.d.ts +42 -0
- package/dist/channels/turn-store.js +139 -0
- package/dist/channels/wait-health.d.ts +6 -0
- package/dist/channels/wait-health.js +27 -0
- 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-add-feishu.d.ts +8 -0
- package/dist/cli-add-feishu.js +223 -0
- package/dist/cli.js +8 -1284
- package/dist/deploy/container.js +10 -6
- 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/fly/plan.d.ts +1 -1
- package/dist/deploy/fly/plan.js +15 -4
- package/dist/deploy/fly/run.d.ts +7 -4
- package/dist/deploy/fly/run.js +26 -5
- package/dist/deploy/preflight.js +4 -3
- package/dist/deploy/railway/plan.d.ts +1 -1
- package/dist/deploy/railway/plan.js +17 -5
- package/dist/deploy/railway/run.d.ts +6 -3
- package/dist/deploy/railway/run.js +26 -4
- package/dist/deploy/registration-gate.d.ts +20 -0
- package/dist/deploy/registration-gate.js +20 -0
- package/dist/deploy/runner.d.ts +4 -1
- package/dist/deploy/runner.js +1 -0
- package/dist/deploy/secrets.d.ts +10 -9
- package/dist/deploy/secrets.js +15 -14
- package/dist/dev-supervisor.js +2 -1
- package/dist/engines/pi/auth.js +160 -46
- package/dist/engines/pi/chat.js +80 -7
- 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 -6
- package/dist/engines/pi/create.js +52 -16
- 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/feishu.d.ts +2 -0
- package/dist/feishu.js +2 -0
- package/dist/lark.d.ts +3 -0
- package/dist/lark.js +3 -0
- package/dist/open-url.d.ts +2 -0
- package/dist/open-url.js +6 -0
- package/dist/pi.d.ts +2 -1
- package/dist/scaffold/add-channel.d.ts +9 -5
- package/dist/scaffold/add-channel.js +75 -7
- package/dist/scaffold/templates/fastagent.config.mjs +1 -0
- package/dist/tunnel.d.ts +9 -6
- package/dist/tunnel.js +48 -31
- package/package.json +18 -4
- /package/dist/channels/{telegram/state.d.ts → state.d.ts} +0 -0
- /package/dist/channels/{telegram/turn-queue.d.ts → turn-queue.d.ts} +0 -0
|
@@ -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>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createInvokeHandler } from "../channels/http.js";
|
|
2
|
+
import { text } from "../channels/respond.js";
|
|
3
|
+
import { loadChannels } from "../engines/pi/channel.js";
|
|
4
|
+
import { reportModuleLoadFailures } from "../engines/pi/report.js";
|
|
5
|
+
import { parseRouteKey, router, serveNode } from "../host/node.js";
|
|
6
|
+
import { log } from "../log.js";
|
|
7
|
+
import { openExternalUrl } from "../open-url.js";
|
|
8
|
+
import { loadSchedules } from "../schedule/discover.js";
|
|
9
|
+
import { createScheduler } from "../schedule/scheduler.js";
|
|
10
|
+
import { announceWebhooks, startCloudflareTunnel } from "../tunnel.js";
|
|
11
|
+
import { failStartup } from "./fail.js";
|
|
12
|
+
/**
|
|
13
|
+
* The routes this deployment serves: a default `GET /health` plus the workspace's discovered
|
|
14
|
+
* `channels/` — or the default invoke channel at POST /invoke when none are declared.
|
|
15
|
+
*/
|
|
16
|
+
export async function routesFor(workspaceDir, agent, stateRoot) {
|
|
17
|
+
const { routes, collisions, failures } = await loadChannels(workspaceDir, { agent, stateRoot });
|
|
18
|
+
for (const c of collisions) {
|
|
19
|
+
console.error(`[fastagent] warn: channel route "${c.route}" (${c.source}) collides with an earlier channel — not mounted`);
|
|
20
|
+
}
|
|
21
|
+
reportModuleLoadFailures(failures);
|
|
22
|
+
if (failures.length > 0 || collisions.length > 0) {
|
|
23
|
+
throw new Error(`channel setup is invalid (${failures.length} load failure(s), ${collisions.length} route collision(s)) — ` +
|
|
24
|
+
`fix it, or rename an intentionally disabled file to *.disabled`);
|
|
25
|
+
}
|
|
26
|
+
const channels = Object.keys(routes).length > 0 ? routes : { "POST /invoke": createInvokeHandler(agent) };
|
|
27
|
+
// Add a default GET /health unless a channel already covers it (overlap, not exact-key: an
|
|
28
|
+
// any-method `/health` also handles GET, so the built-in steps aside).
|
|
29
|
+
const healthCovered = Object.keys(channels).some((k) => {
|
|
30
|
+
const e = parseRouteKey(k);
|
|
31
|
+
return e.path === "/health" && (e.method === undefined || e.method === "GET");
|
|
32
|
+
});
|
|
33
|
+
return healthCovered ? channels : { "GET /health": () => text("ok\n", 200), ...channels };
|
|
34
|
+
}
|
|
35
|
+
/** Serve `routes` via the Node host. serveNode owns binding; the CLI owns policy (errors, ready signal, log). */
|
|
36
|
+
export function serve(routes, port, onListening) {
|
|
37
|
+
serveNode(router(routes), { port }).listening.then((boundPort) => {
|
|
38
|
+
process.send?.({ type: "ready", port: boundPort }); // tell the dev supervisor we bound + on which port
|
|
39
|
+
log.info(`[fastagent] http channel on :${boundPort}`);
|
|
40
|
+
log.info(`[fastagent] routes: ${Object.keys(routes).join(", ") || "(none)"}`);
|
|
41
|
+
onListening?.(boundPort);
|
|
42
|
+
}, (error) => {
|
|
43
|
+
if (error.code === "EADDRINUSE")
|
|
44
|
+
failStartup(new Error(`port ${port} is already in use; choose another with --port`));
|
|
45
|
+
failStartup(new Error(`cannot bind http channel on :${port}: ${error.message}`));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Start a Cloudflare tunnel + announce/register webhooks once the server is bound — unless this is a
|
|
50
|
+
* watch-supervisor worker, where the supervisor owns the long-lived tunnel so the public URL survives
|
|
51
|
+
* reloads.
|
|
52
|
+
*/
|
|
53
|
+
export function maybeTunnel(workspaceDir, boundPort, tunnel) {
|
|
54
|
+
if (!tunnel || process.env.FASTAGENT_DEV_WORKER === "1")
|
|
55
|
+
return;
|
|
56
|
+
void startCloudflareTunnel(boundPort).then((t) => {
|
|
57
|
+
if (!t)
|
|
58
|
+
return;
|
|
59
|
+
void announceWebhooks(workspaceDir, t.url, { openUrl: openExternalUrl });
|
|
60
|
+
// Single-process (start / --no-watch): close the tunnel on exit (watch mode's supervisor owns its own).
|
|
61
|
+
const cleanup = () => {
|
|
62
|
+
t.close();
|
|
63
|
+
process.exit(0);
|
|
64
|
+
};
|
|
65
|
+
process.once("SIGINT", cleanup);
|
|
66
|
+
process.once("SIGTERM", cleanup);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Load and start the workspace's `schedules/` — a time-trigger firing the agent on each cron. Starts iff
|
|
71
|
+
* there are static schedules OR `selfSchedule` is on (the scheduler also polls the agent's self-scheduled
|
|
72
|
+
* wake-ups, which the built-in `wake` tool creates only when opted in). Shares the SAME (trace-wrapped)
|
|
73
|
+
* agent the routes serve, so a scheduled turn is observed like any other. Best-effort stop on exit; dev's
|
|
74
|
+
* watch restart re-reads schedules with the worker (schedules are a code input). Single-process.
|
|
75
|
+
*/
|
|
76
|
+
export async function startSchedules(workspaceDir, agent, stateRoot, selfSchedule) {
|
|
77
|
+
const { schedules, failures } = await loadSchedules(workspaceDir).catch(failStartup);
|
|
78
|
+
reportModuleLoadFailures(failures);
|
|
79
|
+
// Nothing to run when there are neither static `schedules/` nor self-scheduling (the `wake` tool, and
|
|
80
|
+
// thus any wake-up to poll, is mounted only when config.selfSchedule is on) — skip the poller entirely.
|
|
81
|
+
if (schedules.length === 0 && !selfSchedule)
|
|
82
|
+
return;
|
|
83
|
+
const scheduler = createScheduler({ agent, stateRoot, schedules });
|
|
84
|
+
scheduler.start();
|
|
85
|
+
if (schedules.length > 0)
|
|
86
|
+
log.info(`[fastagent] schedules: ${schedules.map((s) => s.name).join(", ")}`);
|
|
87
|
+
const stop = () => scheduler.stop();
|
|
88
|
+
process.once("SIGINT", stop);
|
|
89
|
+
process.once("SIGTERM", stop);
|
|
90
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Both stdin and stdout are a terminal — the precondition for an interactive prompt. */
|
|
2
|
+
export declare function isInteractive(): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Parse + range-check a port string (CLI flag or env). Empty/whitespace is "not set" → undefined, so
|
|
5
|
+
* the `??` chain falls through instead of binding port 0 (`Number("")` is 0). The exit code follows
|
|
6
|
+
* RESPONSIBILITY, not the layer that discovers the problem: a bad `--port` is a usage error (2), a
|
|
7
|
+
* bad `PORT` env is broken runtime configuration (1).
|
|
8
|
+
*/
|
|
9
|
+
export declare function parsePort(value: string | undefined, source: string, from: "flag" | "env"): number | undefined;
|
|
10
|
+
/** Report which source provides the model's credentials, surfacing a remediation hint at startup. Non-blocking. */
|
|
11
|
+
export declare function reportAuth(modelSpec: string, authPath: string): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* First-run model resolution for the serving commands. When no model is set (flag/env/config), and
|
|
14
|
+
* we're on a TTY, pick one from the providers the user is logged into and persist the choice. A no-op
|
|
15
|
+
* when a model is already set; on a non-TTY (CI/deploy), or with `--no-input`, or with nothing
|
|
16
|
+
* configured it stays silent and lets the opener raise its clear "missing model" error. The pick is
|
|
17
|
+
* exported to FASTAGENT_MODEL so a spawned `dev` worker inherits it, and best-effort written back to
|
|
18
|
+
* the config so the next run is quiet.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveFirstRunModel(workspaceDir: string, options?: {
|
|
21
|
+
model?: string;
|
|
22
|
+
authPath?: string;
|
|
23
|
+
input?: boolean;
|
|
24
|
+
}): Promise<void>;
|