@fastagent-sh/fastagent 0.13.0 → 0.15.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.
Files changed (238) hide show
  1. package/README.md +5 -5
  2. package/dist/agent.d.ts +20 -0
  3. package/dist/agent.js +9 -0
  4. package/dist/channels/context-buffer.d.ts +30 -0
  5. package/dist/channels/context-buffer.js +98 -0
  6. package/dist/channels/control.d.ts +28 -0
  7. package/dist/channels/control.js +214 -0
  8. package/dist/channels/feishu/card.js +1 -1
  9. package/dist/channels/feishu/context-buffer.d.ts +43 -0
  10. package/dist/channels/feishu/context-buffer.js +72 -0
  11. package/dist/channels/feishu/crypto.d.ts +4 -2
  12. package/dist/channels/feishu/crypto.js +4 -2
  13. package/dist/channels/feishu/feishu-api.d.ts +15 -7
  14. package/dist/channels/feishu/feishu-api.js +22 -4
  15. package/dist/channels/feishu/feishu.d.ts +38 -16
  16. package/dist/channels/feishu/feishu.js +286 -151
  17. package/dist/channels/feishu/invoke-turn.d.ts +24 -31
  18. package/dist/channels/feishu/invoke-turn.js +61 -62
  19. package/dist/channels/feishu/model.d.ts +98 -0
  20. package/dist/channels/feishu/model.js +9 -0
  21. package/dist/channels/feishu/normalize.d.ts +23 -0
  22. package/dist/channels/feishu/normalize.js +132 -0
  23. package/dist/channels/feishu/owned-threads.d.ts +7 -0
  24. package/dist/channels/feishu/owned-threads.js +47 -0
  25. package/dist/channels/feishu/parse.d.ts +21 -103
  26. package/dist/channels/feishu/parse.js +35 -145
  27. package/dist/channels/feishu/preview.d.ts +4 -7
  28. package/dist/channels/feishu/preview.js +26 -142
  29. package/dist/channels/feishu/register-app.d.ts +2 -1
  30. package/dist/channels/feishu/register-webhook.d.ts +2 -1
  31. package/dist/channels/feishu/scaffold/channel.ts +10 -3
  32. package/dist/channels/feishu/setup-mode.d.ts +8 -0
  33. package/dist/channels/feishu/setup-mode.js +2 -0
  34. package/dist/channels/feishu/ws-ingress.d.ts +28 -0
  35. package/dist/channels/feishu/ws-ingress.js +136 -0
  36. package/dist/channels/github/github.js +8 -6
  37. package/dist/channels/http.d.ts +14 -0
  38. package/dist/channels/http.js +35 -2
  39. package/dist/channels/invoke-turn-kit.d.ts +65 -0
  40. package/dist/channels/invoke-turn-kit.js +87 -0
  41. package/dist/channels/lark/lark.d.ts +4 -2
  42. package/dist/channels/lark/lark.js +4 -1
  43. package/dist/channels/lark/onboard.d.ts +8 -4
  44. package/dist/channels/lark/onboard.js +8 -0
  45. package/dist/channels/lark/scaffold/channel.ts +10 -3
  46. package/dist/channels/preview-kit.d.ts +109 -0
  47. package/dist/channels/preview-kit.js +183 -0
  48. package/dist/channels/seen.d.ts +5 -0
  49. package/dist/channels/seen.js +35 -0
  50. package/dist/channels/slack/bot-auth.d.ts +15 -0
  51. package/dist/channels/slack/bot-auth.js +146 -0
  52. package/dist/channels/slack/config-api.d.ts +60 -0
  53. package/dist/channels/slack/config-api.js +149 -0
  54. package/dist/channels/slack/context-buffer.d.ts +24 -0
  55. package/dist/channels/slack/context-buffer.js +37 -0
  56. package/dist/channels/slack/invoke-turn.d.ts +19 -0
  57. package/dist/channels/slack/invoke-turn.js +63 -0
  58. package/dist/channels/slack/manifest.d.ts +49 -0
  59. package/dist/channels/slack/manifest.js +69 -0
  60. package/dist/channels/slack/model.d.ts +67 -0
  61. package/dist/channels/slack/model.js +2 -0
  62. package/dist/channels/slack/onboard.d.ts +41 -0
  63. package/dist/channels/slack/onboard.js +120 -0
  64. package/dist/channels/slack/onboarding-state.d.ts +31 -0
  65. package/dist/channels/slack/onboarding-state.js +69 -0
  66. package/dist/channels/slack/owned-threads.d.ts +6 -0
  67. package/dist/channels/slack/owned-threads.js +43 -0
  68. package/dist/channels/slack/parse.d.ts +23 -0
  69. package/dist/channels/slack/parse.js +81 -0
  70. package/dist/channels/slack/preview.d.ts +24 -0
  71. package/dist/channels/slack/preview.js +359 -0
  72. package/dist/channels/slack/reaction.d.ts +24 -0
  73. package/dist/channels/slack/reaction.js +62 -0
  74. package/dist/channels/slack/register-webhook.d.ts +10 -0
  75. package/dist/channels/slack/register-webhook.js +49 -0
  76. package/dist/channels/slack/scaffold/channel.ts +33 -0
  77. package/dist/channels/slack/scaffold/slack-send.ts +171 -0
  78. package/dist/channels/slack/setup-server.d.ts +17 -0
  79. package/dist/channels/slack/setup-server.js +103 -0
  80. package/dist/channels/slack/slack-api.d.ts +77 -0
  81. package/dist/channels/slack/slack-api.js +415 -0
  82. package/dist/channels/slack/slack.d.ts +58 -0
  83. package/dist/channels/slack/slack.js +451 -0
  84. package/dist/channels/slack/welcomed.d.ts +5 -0
  85. package/dist/channels/slack/welcomed.js +32 -0
  86. package/dist/channels/state.js +3 -3
  87. package/dist/channels/stop-command.d.ts +6 -0
  88. package/dist/channels/stop-command.js +36 -0
  89. package/dist/channels/tasks.d.ts +13 -0
  90. package/dist/channels/tasks.js +10 -0
  91. package/dist/channels/telegram/context-buffer.d.ts +8 -17
  92. package/dist/channels/telegram/context-buffer.js +6 -85
  93. package/dist/channels/telegram/invoke-turn.d.ts +5 -22
  94. package/dist/channels/telegram/invoke-turn.js +11 -58
  95. package/dist/channels/telegram/preview.d.ts +4 -7
  96. package/dist/channels/telegram/preview.js +24 -142
  97. package/dist/channels/telegram/telegram.js +23 -9
  98. package/dist/channels/{feishu/text.d.ts → text.d.ts} +1 -1
  99. package/dist/channels/{feishu/text.js → text.js} +1 -1
  100. package/dist/channels/turn-queue.js +1 -1
  101. package/dist/channels/turn-store.d.ts +1 -1
  102. package/dist/channels/turn-store.js +2 -3
  103. package/dist/cli/add-feishu.d.ts +27 -0
  104. package/dist/{cli-add-feishu.js → cli/add-feishu.js} +167 -44
  105. package/dist/cli/add-slack.d.ts +10 -0
  106. package/dist/cli/add-slack.js +204 -0
  107. package/dist/{cli-auth.d.ts → cli/auth-view.d.ts} +1 -1
  108. package/dist/{cli-auth.js → cli/auth-view.js} +1 -1
  109. package/dist/cli/commands/add.d.ts +13 -0
  110. package/dist/cli/commands/add.js +274 -0
  111. package/dist/cli/commands/attach.d.ts +82 -0
  112. package/dist/cli/commands/attach.js +559 -0
  113. package/dist/cli/commands/chat.d.ts +4 -0
  114. package/dist/cli/commands/chat.js +21 -0
  115. package/dist/cli/commands/deploy.d.ts +15 -0
  116. package/dist/cli/commands/deploy.js +394 -0
  117. package/dist/cli/commands/dev.d.ts +11 -0
  118. package/dist/cli/commands/dev.js +82 -0
  119. package/dist/cli/commands/fire.d.ts +7 -0
  120. package/dist/cli/commands/fire.js +45 -0
  121. package/dist/cli/commands/info.d.ts +7 -0
  122. package/dist/cli/commands/info.js +108 -0
  123. package/dist/cli/commands/init.d.ts +8 -0
  124. package/dist/cli/commands/init.js +81 -0
  125. package/dist/cli/commands/invoke.d.ts +7 -0
  126. package/dist/cli/commands/invoke.js +28 -0
  127. package/dist/cli/commands/login.d.ts +6 -0
  128. package/dist/cli/commands/login.js +52 -0
  129. package/dist/cli/commands/models.d.ts +1 -0
  130. package/dist/cli/commands/models.js +15 -0
  131. package/dist/cli/commands/schedule.d.ts +12 -0
  132. package/dist/cli/commands/schedule.js +89 -0
  133. package/dist/cli/commands/start.d.ts +10 -0
  134. package/dist/cli/commands/start.js +97 -0
  135. package/dist/cli/commands/tool.d.ts +1 -0
  136. package/dist/cli/commands/tool.js +38 -0
  137. package/dist/cli/fail.d.ts +17 -0
  138. package/dist/cli/fail.js +32 -0
  139. package/dist/{invoke-stream.d.ts → cli/invoke-stream.d.ts} +1 -1
  140. package/dist/{invoke-stream.js → cli/invoke-stream.js} +4 -0
  141. package/dist/cli/kernel.d.ts +90 -0
  142. package/dist/cli/kernel.js +190 -0
  143. package/dist/cli/models-view.d.ts +21 -0
  144. package/dist/cli/models-view.js +66 -0
  145. package/dist/cli/program.d.ts +11 -0
  146. package/dist/cli/program.js +479 -0
  147. package/dist/cli/serve.d.ts +48 -0
  148. package/dist/cli/serve.js +248 -0
  149. package/dist/cli/shared.d.ts +44 -0
  150. package/dist/cli/shared.js +246 -0
  151. package/dist/cli.js +8 -1329
  152. package/dist/collect.d.ts +14 -3
  153. package/dist/collect.js +24 -0
  154. package/dist/core.d.ts +3 -1
  155. package/dist/core.js +2 -0
  156. package/dist/deploy/docker/plan.d.ts +45 -0
  157. package/dist/deploy/docker/plan.js +141 -0
  158. package/dist/deploy/docker/run.d.ts +40 -0
  159. package/dist/deploy/docker/run.js +126 -0
  160. package/dist/deploy/fly/plan.d.ts +3 -1
  161. package/dist/deploy/fly/plan.js +12 -7
  162. package/dist/deploy/fly/run.d.ts +5 -4
  163. package/dist/deploy/fly/run.js +16 -5
  164. package/dist/deploy/preflight.d.ts +10 -4
  165. package/dist/deploy/preflight.js +57 -12
  166. package/dist/deploy/railway/plan.d.ts +3 -1
  167. package/dist/deploy/railway/plan.js +12 -4
  168. package/dist/deploy/railway/run.d.ts +5 -4
  169. package/dist/deploy/railway/run.js +15 -5
  170. package/dist/deploy/runner.d.ts +6 -2
  171. package/dist/deploy/runner.js +1 -0
  172. package/dist/deploy/secrets.d.ts +2 -1
  173. package/dist/deploy/secrets.js +23 -3
  174. package/dist/dev-supervisor.d.ts +0 -2
  175. package/dist/dev-supervisor.js +7 -3
  176. package/dist/engines/pi/auth.js +160 -46
  177. package/dist/engines/pi/channel.d.ts +22 -16
  178. package/dist/engines/pi/channel.js +90 -60
  179. package/dist/engines/pi/chat.d.ts +4 -16
  180. package/dist/engines/pi/chat.js +8 -188
  181. package/dist/engines/pi/config.d.ts +23 -9
  182. package/dist/engines/pi/config.js +35 -5
  183. package/dist/engines/pi/create.d.ts +36 -7
  184. package/dist/engines/pi/create.js +63 -22
  185. package/dist/engines/pi/harness.d.ts +65 -1
  186. package/dist/engines/pi/harness.js +166 -2
  187. package/dist/engines/pi/invoke.d.ts +56 -3
  188. package/dist/engines/pi/invoke.js +340 -20
  189. package/dist/engines/pi/login.d.ts +11 -0
  190. package/dist/engines/pi/login.js +17 -5
  191. package/dist/engines/pi/models.d.ts +56 -10
  192. package/dist/engines/pi/models.js +61 -23
  193. package/dist/engines/pi/search-tools.d.ts +10 -0
  194. package/dist/engines/pi/search-tools.js +138 -0
  195. package/dist/engines/pi/session-builder.d.ts +16 -0
  196. package/dist/engines/pi/session-builder.js +308 -0
  197. package/dist/engines/pi/session-control.d.ts +50 -0
  198. package/dist/engines/pi/session-control.js +604 -0
  199. package/dist/engines/pi/sessions.d.ts +17 -2
  200. package/dist/engines/pi/sessions.js +9 -0
  201. package/dist/engines/pi/tool-context.d.ts +46 -11
  202. package/dist/engines/pi/tool-context.js +11 -9
  203. package/dist/engines/pi/tool.d.ts +35 -6
  204. package/dist/engines/pi/tool.js +47 -1
  205. package/dist/engines/pi/wake-tool.d.ts +0 -3
  206. package/dist/engines/pi/wake-tool.js +9 -7
  207. package/dist/engines/pi/workspace.d.ts +56 -1
  208. package/dist/engines/pi/workspace.js +75 -16
  209. package/dist/feishu.d.ts +1 -1
  210. package/dist/feishu.js +1 -1
  211. package/dist/host/node.d.ts +23 -6
  212. package/dist/host/node.js +5 -4
  213. package/dist/index.d.ts +1 -0
  214. package/dist/index.js +1 -0
  215. package/dist/lark.d.ts +1 -1
  216. package/dist/lark.js +1 -1
  217. package/dist/observe.js +3 -0
  218. package/dist/pi.d.ts +6 -4
  219. package/dist/pi.js +2 -1
  220. package/dist/scaffold/add-channel.d.ts +19 -6
  221. package/dist/scaffold/add-channel.js +127 -16
  222. package/dist/scaffold/templates/fastagent.config.mjs +5 -3
  223. package/dist/schedule/wakeups.d.ts +0 -3
  224. package/dist/schedule/wakeups.js +1 -1
  225. package/dist/session-remote.d.ts +53 -0
  226. package/dist/session-remote.js +336 -0
  227. package/dist/session.d.ts +265 -0
  228. package/dist/session.js +37 -0
  229. package/dist/slack.d.ts +2 -0
  230. package/dist/slack.js +2 -0
  231. package/dist/tunnel.d.ts +7 -4
  232. package/dist/tunnel.js +21 -10
  233. package/package.json +22 -7
  234. package/dist/channels/feishu/seen.d.ts +0 -5
  235. package/dist/channels/feishu/seen.js +0 -47
  236. package/dist/cli-add-feishu.d.ts +0 -8
  237. package/dist/cli-models.d.ts +0 -11
  238. package/dist/cli-models.js +0 -20
@@ -0,0 +1,479 @@
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, onboarded slack, feishu, lark; github/manual slack print 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 + registered/guided 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 attach = {
92
+ name: "attach",
93
+ summary: "watch a session's live events from a running serve and steer it",
94
+ description: "Attach to a session served by a running dev/start with `sessionControl: true` in the config: " +
95
+ "stream its live events (text, tools, run boundaries), steer the active run by typing — or, with " +
96
+ "no run active, start one (detaching cancels a run YOU started) — /abort to stop a run. Discovers " +
97
+ "the local endpoint from <stateRoot>/control.json; --url/--token reach a remote serve. The same " +
98
+ "wire protocol a Web panel or desktop app uses.",
99
+ args: [{ name: "<session>", description: "the session id to attach to" }, DIR_ARG],
100
+ flags: [
101
+ { flags: "--url <url>", description: "control endpoint (skip control.json discovery)" },
102
+ { flags: "--token <token>", description: "bearer token for --url" },
103
+ ],
104
+ examples: [{ cmd: "fastagent attach tg-chat-42" }],
105
+ run: async (args, f) => (await import("./commands/attach.js")).runAttach(args[0], args[1], {
106
+ url: f.url,
107
+ token: f.token,
108
+ }),
109
+ };
110
+ const chat = {
111
+ name: "chat",
112
+ summary: "open the SAME assembled agent in pi's interactive TUI",
113
+ description: "Open the SAME assembled agent in pi's interactive TUI (the real harness, not a crude REPL) — to " +
114
+ "try it locally before serving. Same model/tool/skill/auth resolution as dev; pi handles " +
115
+ "rendering, sessions, and /resume natively (its /login writes to the same fastagent auth file).",
116
+ args: [DIR_ARG],
117
+ flags: [MODEL, AUTH_PATH],
118
+ examples: [{ cmd: "fastagent chat" }],
119
+ run: async (args, f) => (await import("./commands/chat.js")).runChat(args[0], {
120
+ model: f.model,
121
+ authPath: f.authPath,
122
+ }),
123
+ };
124
+ const info = {
125
+ name: "info",
126
+ summary: "print what the directory assembles into, without serving",
127
+ description: "Print what dir (default .) ASSEMBLES into — model, persona, context files (AGENTS.md), skills, " +
128
+ "tools (+ collisions), channels, schedules, sessions, load diagnostics — WITHOUT serving. " +
129
+ "Read-only (never creates sessions / writes .gitignore); an unset model is reported, not fatal. " +
130
+ "Run it first when something looks off.",
131
+ args: [DIR_ARG],
132
+ flags: [JSON_FLAG, MODEL, AUTH_PATH, { flags: "--sessions-dir <dir>", description: "sessions directory override" }],
133
+ examples: [{ cmd: "fastagent info" }, { cmd: "fastagent info --json", note: "for CI" }],
134
+ run: async (args, f) => (await import("./commands/info.js")).runInfo(args[0], {
135
+ json: f.json === true,
136
+ model: f.model,
137
+ authPath: f.authPath,
138
+ sessionsDir: f.sessionsDir,
139
+ }),
140
+ };
141
+ const tool = {
142
+ name: "tool",
143
+ summary: "run one tool directly with JSON args — no model, no server, no tokens",
144
+ description: "Run one tool (from tools/ or config.tools) directly with JSON args — no model, no server, no " +
145
+ "tokens. Fast feedback while authoring a tool.",
146
+ args: [
147
+ { name: "<name>", description: "the tool name as served (see `fastagent info`)" },
148
+ { name: "[json-args]", description: "the tool's arguments as a JSON object", default: "{}" },
149
+ DIR_ARG,
150
+ ],
151
+ examples: [{ cmd: `fastagent tool add '{"a":2,"b":3}'` }],
152
+ notes: "Mounts the same tool set dev/start serve (defaults + config.tools + discovered tools/, " +
153
+ "deduped), so a shadowed or broken tool is surfaced here exactly as it would be when serving.",
154
+ run: async (args) => (await import("./commands/tool.js")).runTool(args[0], args[1], args[2]),
155
+ };
156
+ const invoke = {
157
+ name: "invoke",
158
+ summary: "run ONE turn against the assembled agent and exit",
159
+ description: "Run ONE turn against the assembled agent and exit — no server, no TUI. The reply streams to " +
160
+ "stdout, tool/diagnostics to stderr, a failed turn exits non-zero. The all-agent counterpart of " +
161
+ "`tool`, for CI smoke and quick checks. Same model resolution as dev.",
162
+ args: [{ name: "<message>", description: "the user message for the turn" }, DIR_ARG],
163
+ flags: [MODEL, AUTH_PATH, NO_INPUT],
164
+ examples: [{ cmd: `fastagent invoke "summarize today's inbox"` }],
165
+ run: async (args, f) => (await import("./commands/invoke.js")).runInvoke(args[0], args[1], {
166
+ model: f.model,
167
+ authPath: f.authPath,
168
+ input: f.input !== false,
169
+ }),
170
+ };
171
+ const fire = {
172
+ name: "fire",
173
+ summary: "run ONE schedule's turn immediately, without waiting for its cron",
174
+ description: "Run ONE schedule's turn immediately (authoring loop, like invoke) — fires schedules/<name>.ts now " +
175
+ "without waiting for its cron. Reply→stdout; does NOT advance the schedule's fire state.",
176
+ args: [{ name: "<name>", description: "the schedule name (schedules/<name>.ts)" }, DIR_ARG],
177
+ flags: [MODEL, AUTH_PATH, NO_INPUT],
178
+ examples: [{ cmd: "fastagent fire daily-digest" }],
179
+ run: async (args, f) => (await import("./commands/fire.js")).runFire(args[0], args[1], {
180
+ model: f.model,
181
+ authPath: f.authPath,
182
+ input: f.input !== false,
183
+ }),
184
+ };
185
+ const models = {
186
+ name: "models",
187
+ summary: 'list the available "provider/modelId" model specs',
188
+ description: 'List every registered "provider/modelId" spec — use one with --model or as `model` in fastagent.config.ts.',
189
+ args: [{ name: "[search]", description: "case-insensitive substring filter" }],
190
+ examples: [
191
+ { cmd: "fastagent models", note: "all specs" },
192
+ { cmd: "fastagent models claude", note: "filter; provider-name matches rank first" },
193
+ ],
194
+ run: async (args) => (await import("./commands/models.js")).runModels(args[0]),
195
+ };
196
+ const start = {
197
+ name: "start",
198
+ summary: "run the agent in production posture (same assembly as dev, no watching)",
199
+ description: "Run the agent in dir (default .) in production posture — the SAME assembly as dev (your directory " +
200
+ "is the agent), just no file-watching. No build step: start reads the definition directly; " +
201
+ "model/http come from fastagent.config.ts (frozen by git).",
202
+ args: [DIR_ARG],
203
+ flags: [
204
+ PORT,
205
+ MODEL,
206
+ { flags: "--sessions-dir <dir>", description: "sessions directory override" },
207
+ AUTH_PATH,
208
+ TUNNEL,
209
+ NO_INPUT,
210
+ ],
211
+ examples: [
212
+ { cmd: "fastagent start" },
213
+ { cmd: "fastagent start --tunnel", note: "host a bot from your own box, no deploy" },
214
+ ],
215
+ notes: "Precedence chains:\n" +
216
+ " port: --port > PORT env > fastagent.config.ts http.port > 8787\n" +
217
+ " state: FASTAGENT_STATE_DIR > <dir>/.fastagent — the ONE machine-state\n" +
218
+ " root (auth, sessions, channel state all derive from it); point\n" +
219
+ " it at a mounted volume so a redeploy that replaces the\n" +
220
+ " directory never wipes state\n" +
221
+ " sessions: --sessions-dir > FASTAGENT_SESSIONS_DIR > <state>/sessions\n" +
222
+ " auth: --auth-path > FASTAGENT_AUTH_PATH > <state>/auth.json\n" +
223
+ " (project-level; point it at ~/.fastagent/auth.json to share one\n" +
224
+ " credential across projects)",
225
+ run: async (args, f) => (await import("./commands/start.js")).runStart(args[0], {
226
+ port: f.port,
227
+ model: f.model,
228
+ sessionsDir: f.sessionsDir,
229
+ authPath: f.authPath,
230
+ tunnel: f.tunnel === true,
231
+ input: f.input !== false,
232
+ }),
233
+ };
234
+ /** The retired app-creation flag — parsed so it can explain itself, hidden from help. */
235
+ const CREATE_APP = { flags: "--create-app", description: "(retired)", hidden: true };
236
+ const INGRESS = {
237
+ flags: "--ingress <mode>",
238
+ description: "Feishu/Lark ingress: websocket or webhook (interactive when omitted)",
239
+ };
240
+ const GROUP_BEHAVIOR = {
241
+ flags: "--group-behavior <behavior>",
242
+ description: "Slack/Feishu/Lark groups: context (recommended) or mentions (least privilege)",
243
+ };
244
+ const NO_ONBOARD = {
245
+ flags: "--no-onboard",
246
+ description: "Slack: scaffold only; skip internal-app creation/OAuth",
247
+ };
248
+ const REPLACE_CONFIG = {
249
+ flags: "--replace-config",
250
+ description: "Slack: replace the local App Configuration token pair (repairs automatic dev/deploy Request URL " +
251
+ "updates after the tokens expire or are revoked; runs on the machine that onboarded the app)",
252
+ };
253
+ const channelSub = (kind, summary, description, notes) => ({
254
+ name: kind,
255
+ summary,
256
+ description,
257
+ args: [DIR_ARG],
258
+ flags: [
259
+ CREATE_APP,
260
+ ...(kind === "feishu" || kind === "lark"
261
+ ? [INGRESS, GROUP_BEHAVIOR]
262
+ : kind === "slack"
263
+ ? [GROUP_BEHAVIOR, NO_ONBOARD, REPLACE_CONFIG]
264
+ : []),
265
+ ],
266
+ examples: [{ cmd: `fastagent add ${kind}` }],
267
+ ...(notes ? { notes } : {}),
268
+ run: async (args, f) => (await import("./commands/add.js")).runAddChannel(kind, args[0], {
269
+ createApp: f.createApp === true,
270
+ ingress: f.ingress,
271
+ groupBehavior: f.groupBehavior,
272
+ onboard: f.onboard !== false,
273
+ replaceConfig: f.replaceConfig === true,
274
+ }),
275
+ });
276
+ const add = {
277
+ name: "add",
278
+ summary: "connect a channel (github, telegram, slack, feishu, lark) or vendor a skill",
279
+ description: "Scaffold channels/<kind>.ts — first-party adapter glue with the policy to edit (github maps " +
280
+ "events in on(); telegram/slack/feishu/lark route in the optional route()) — or vendor an Agent Skills " +
281
+ "skill into skills/<name>/.",
282
+ subcommands: [
283
+ channelSub("github", "scaffold the GitHub webhook channel (issues/PRs → agent turns)", "Scaffold channels/github.ts — webhook adapter glue that maps repository events (issues, PRs, " +
284
+ "comments) to agent turns in its on() policy."),
285
+ 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 " +
286
+ "message pump, and an optional route() policy."),
287
+ channelSub("slack", "scaffold the Slack Events API channel (files, threads, context, live preview)", "Choose group visibility, scaffold channels/slack.ts plus slack-send.ts, create a single-workspace " +
288
+ "internal Slack app from a manifest, and install it through OAuth. The channel provides signed " +
289
+ "Events API ingress, durable turns, files, managed threads, context, and an edited live preview.", "Automated onboarding requires Slack App Configuration access + refresh tokens and a temporary " +
290
+ "cloudflared tunnel. They stay in owner-readable local state and are never deployed; --no-onboard " +
291
+ "keeps the explicit manual/scaffold-only path."),
292
+ channelSub("feishu", "scaffold the Feishu channel AND create/configure the platform app", "Choose WebSocket or webhook, scaffold channels/feishu.ts, and create/configure the Feishu app " +
293
+ "through scan-to-create, writing the matching credentials to .env.", "Feishu (open.feishu.cn) is the canonical implementation. WebSocket needs only App ID/Secret and " +
294
+ "no public URL; webhook additionally captures the Verification Token through a temporary tunnel. " +
295
+ "Context-aware groups (recommended) request admin approval for im:message.group_msg before publish."),
296
+ channelSub("lark", "scaffold the Lark (international) channel with guided credential setup", "Choose WebSocket or webhook, scaffold channels/lark.ts, and guide credential setup against the " +
297
+ "international developer console.", "Lark international (open.larksuite.com) is Feishu's compatibility profile. WebSocket stops after " +
298
+ "App ID/Secret validation; webhook and recommended context-aware group setup probe config " +
299
+ "automation and fall back to explicit manual steps on the international config-route 404."),
300
+ {
301
+ name: "skill",
302
+ summary: "vendor an Agent Skills skill into skills/<name>/ (copied in, git-tracked)",
303
+ args: [
304
+ {
305
+ name: "[source]",
306
+ description: "a git ref (owner/repo/path, github default), a local path (./x, /abs), or a bare name " +
307
+ "from your global skill dirs (~/.agents/skills, ~/.pi/agent/skills)",
308
+ },
309
+ DIR_ARG,
310
+ ],
311
+ flags: [
312
+ { flags: "--update", description: "overwrite an existing skill (re-fetch from source); review with git diff" },
313
+ ],
314
+ examples: [
315
+ { cmd: "fastagent add skill anthropics/skills/document-skills/pdf" },
316
+ { cmd: "fastagent add skill ./my-skill --update" },
317
+ ],
318
+ notes: "Writing your own skill needs no command: create skills/<name>/SKILL.md with name + " +
319
+ "description frontmatter; it's auto-discovered. `add skill` is only for vendoring an " +
320
+ "existing one.",
321
+ run: async (args, f) => (await import("./commands/add.js")).runAddSkill(args[0], args[1], { update: f.update === true }),
322
+ },
323
+ ],
324
+ };
325
+ const deploy = {
326
+ name: "deploy",
327
+ summary: "generate deploy artifacts + a runbook for docker, fly, or railway (--run drives it)",
328
+ description: "Generate Dockerfile/.dockerignore plus the target config and print an ordered runbook. " +
329
+ "docker: fastagent.compose.yml, loopback port, persistent state volume. fly: fly.toml " +
330
+ "(autostop=suspend, state→volume). railway: railway.json (healthcheck /health); its " +
331
+ "volume/variables/App-Sleeping are dashboard/CLI steps the runbook states. Durable ingress " +
332
+ "remains operator-owned.",
333
+ args: [{ name: "<host>", description: "deploy target", choices: ["docker", "fly", "railway"] }, DIR_ARG],
334
+ flags: [
335
+ {
336
+ flags: "--run",
337
+ description: "drive the target CLI to completion. Docker runs `docker compose up -d --build`; with a tunnel " +
338
+ "service, reads its URL and registers webhooks. Fly/Railway provision app/service + volume + " +
339
+ "secrets + deploy + webhook setup. Carries your local credential (env key or OAuth auth.json). " +
340
+ "Stops at a gate (missing CLI/daemon/login/secret) with one actionable line. Without it: prints " +
341
+ "the runbook",
342
+ },
343
+ {
344
+ flags: "--tunnel",
345
+ description: "(docker only) add a Quick Tunnel service to generated Compose; generation-only unless combined " +
346
+ "with --run. Existing Compose stays authoritative",
347
+ },
348
+ { flags: "--force", description: "overwrite existing target config/Dockerfile/.dockerignore (else kept)" },
349
+ {
350
+ flags: "--stop",
351
+ description: "(fly only) autostop by stopping (cold start) instead of suspending (fast resume)",
352
+ },
353
+ {
354
+ flags: "--no-scale-to-zero",
355
+ description: "(fly only) keep one machine running when idle (min_machines_running=1)",
356
+ },
357
+ {
358
+ flags: "--into-linked",
359
+ description: "(railway --run) provision INTO the project this dir is already linked to (skip create); by " +
360
+ "default --run refuses a pre-existing link (could be unrelated/production)",
361
+ },
362
+ MODEL,
363
+ AUTH_PATH,
364
+ NO_INPUT,
365
+ ],
366
+ examples: [
367
+ { cmd: "fastagent deploy fly --run", note: "provision + deploy + webhooks" },
368
+ { cmd: "fastagent deploy docker --tunnel --run", note: "Compose + a public URL" },
369
+ { cmd: "fastagent deploy railway", note: "print the runbook only" },
370
+ ],
371
+ notes: "Definition-read-only: the only writes are generated artifacts (never clobbered without " +
372
+ "--force). A routine redeploy of an already-provisioned agent is just the host's own command " +
373
+ "(e.g. `railway up`).",
374
+ run: async (args, f) => (await import("./commands/deploy.js")).runDeploy(args[0], args[1], {
375
+ run: f.run === true,
376
+ tunnel: f.tunnel === true,
377
+ force: f.force === true,
378
+ stop: f.stop === true,
379
+ scaleToZero: f.scaleToZero !== false,
380
+ intoLinked: f.intoLinked === true,
381
+ model: f.model,
382
+ authPath: f.authPath,
383
+ input: f.input !== false,
384
+ }),
385
+ };
386
+ const schedule = {
387
+ name: "schedule",
388
+ summary: "inspect and control time triggers: run audit, pending fires, cancel a wake-up",
389
+ subcommands: [
390
+ {
391
+ name: "history",
392
+ summary: 'print the run audit for a schedule (or "wake" for self-scheduled wake-ups)',
393
+ description: "Print the run audit for a schedule: when each run fired, completed/failed/deferred, duration, and " +
394
+ 'the reply/error — the answer to "did last night\'s run silently fail?". Read-only.',
395
+ args: [
396
+ { name: "<name>", description: 'the schedule name, or "wake" for the agent\'s self-scheduled wake-ups' },
397
+ DIR_ARG,
398
+ ],
399
+ flags: [{ flags: "--json", description: "the full records (complete reply text)" }],
400
+ examples: [
401
+ { cmd: "fastagent schedule history daily-digest" },
402
+ { cmd: "fastagent schedule history wake --json", note: "the agent's own wake-ups" },
403
+ ],
404
+ run: async (args, flags) => (await import("./commands/schedule.js")).runScheduleHistory(args[0], args[1], flags.json === true),
405
+ },
406
+ {
407
+ name: "list",
408
+ summary: "everything that will fire: static schedules (next instant) + pending wake-ups",
409
+ description: "List everything that will fire, from BOTH producers: the static schedules/ files (name + next " +
410
+ "cron instant) and the agent's pending self-scheduled wake-ups. Read-only.",
411
+ args: [DIR_ARG],
412
+ flags: [JSON_FLAG],
413
+ examples: [{ cmd: "fastagent schedule list" }],
414
+ run: async (args, flags) => (await import("./commands/schedule.js")).runScheduleList(args[0], flags.json === true),
415
+ },
416
+ {
417
+ name: "cancel",
418
+ summary: "remove a pending wake-up — the operator's kill switch for a runaway recurring wake",
419
+ description: "Remove a pending wake-up — the operator's kill switch for a runaway recurring wake (the agent's own " +
420
+ "is the `unwake` tool). Not session-scoped: the operator owns the box.",
421
+ args: [{ name: "<id>", description: "the wake-up id (`fastagent schedule list` shows ids)" }, DIR_ARG],
422
+ examples: [{ cmd: "fastagent schedule cancel wake-1700000000000-ab12" }],
423
+ run: async (args) => (await import("./commands/schedule.js")).runScheduleCancel(args[0], args[1]),
424
+ },
425
+ ],
426
+ };
427
+ const login = {
428
+ name: "login",
429
+ summary: "authenticate a model provider (subscription/OAuth or API key)",
430
+ description: "Authenticate a model provider into the project-level <state root>/auth.json — default " +
431
+ "<cwd>/.fastagent/auth.json (run from $HOME for the global ~/.fastagent/auth.json): pick a method " +
432
+ "(subscription/OAuth or API key), then a provider that offers it (configured status shown). " +
433
+ "[provider] takes the method from what that provider supports, asked only when both.",
434
+ args: [{ name: "[provider]", description: "provider id (skip the provider menu)" }],
435
+ flags: [AUTH_PATH, NO_INPUT],
436
+ examples: [{ cmd: "fastagent login" }, { cmd: "fastagent login openai" }],
437
+ notes: "The positional is the PROVIDER (not a dir) — `cd` into your agent before logging in.",
438
+ run: async (args, f) => (await import("./commands/login.js")).runLogin(args[0], {
439
+ authPath: f.authPath,
440
+ input: f.input !== false,
441
+ }),
442
+ };
443
+ /** Registration order = help order — the ORIGINAL usage wall's order, kept verbatim (this is a
444
+ * commander refactor of the same CLI, not a redesign). Exported for the kernel conformance tests. */
445
+ export const specs = [
446
+ init,
447
+ models,
448
+ info,
449
+ tool,
450
+ invoke,
451
+ fire,
452
+ schedule,
453
+ dev,
454
+ chat,
455
+ attach,
456
+ start,
457
+ add,
458
+ deploy,
459
+ login,
460
+ ];
461
+ /**
462
+ * The production program assembly (specs + the top-level examples/docs). Tests build through THIS —
463
+ * with their IO/width/color seams as overrides — so they exercise the real shape, not a lookalike.
464
+ */
465
+ export function buildCliProgram(overrides = {}) {
466
+ return buildProgram(specs, {
467
+ examples: [
468
+ { cmd: "fastagent init my-agent && cd my-agent", note: "scaffold an agent" },
469
+ { cmd: "fastagent dev", note: "serve locally and iterate" },
470
+ { cmd: "fastagent deploy fly --run", note: "ship it" },
471
+ ],
472
+ notes: "Docs: https://github.com/fastagent-sh/fastagent",
473
+ ...overrides,
474
+ });
475
+ }
476
+ /** Parse and run one CLI invocation (`argv` = process.argv). Usage errors exit 2 via the kernel policy. */
477
+ export async function runCli(argv) {
478
+ await buildCliProgram({ version: await fastagentVersion() }).parseAsync([...argv]);
479
+ }
@@ -0,0 +1,48 @@
1
+ import type { Agent } from "../agent.ts";
2
+ import { type LoadedLongConnectionChannel } from "../engines/pi/channel.ts";
3
+ import { type Routes } from "../host/node.ts";
4
+ import type { SessionControl } from "../session.ts";
5
+ export interface ServingSurface {
6
+ routes: Routes;
7
+ longConnections: LoadedLongConnectionChannel[];
8
+ /** Route-channel basenames; the tunnel registers only this subset. */
9
+ routeChannels: string[];
10
+ builtinInvoke: boolean;
11
+ /** Marks the built-in health route ready after every long-connection channel first connects. */
12
+ markReady(): void;
13
+ }
14
+ /**
15
+ * The surface this deployment serves: default `GET /health` plus discovered channels, or the default
16
+ * POST `/invoke` only when neither a route nor a long-connection channel was declared.
17
+ */
18
+ export declare function routesFor(workspaceDir: string, agent: Agent, stateRoot: string, control?: SessionControl): Promise<ServingSurface>;
19
+ /**
20
+ * Mount the session control plane (`/control/*`) when the workspace enabled it
21
+ * (`config.sessionControl`): merge the bearer-authenticated routes and return an announcer that
22
+ * writes `<stateRoot>/control.json` — `{ url, token }`, 0600 — once the port is known. The file is
23
+ * the LOCAL discovery channel (`fastagent attach`, a local desktop app); filesystem permissions are
24
+ * its trust boundary, and each boot overwrites it with a fresh per-boot token. A user channel
25
+ * colliding on `/control/*` fails startup — the same disposition as a channel-channel collision
26
+ * (routesFor): `sessionControl` is an explicit opt-in, so declaring both is a configuration error,
27
+ * and silently shadowing either side would serve a surface the author didn't write.
28
+ */
29
+ export declare function mountSessionControl(routes: Routes, control: SessionControl | undefined, stateRoot: string, options?: {
30
+ tunnel?: boolean;
31
+ agent?: Agent;
32
+ }): {
33
+ routes: Routes;
34
+ announce: (boundPort: number) => void;
35
+ };
36
+ /**
37
+ * Bind HTTP, open long-connection channels, and report ready only when both forms are usable. Each
38
+ * adapter owns reconnects; a terminal close rejects `closed` and fails the process visibly. Abort is
39
+ * the sole clean-shutdown command.
40
+ */
41
+ export declare function serve(surface: ServingSurface, port: number, onListening?: (boundPort: number) => void): void;
42
+ /** Start a Cloudflare tunnel for route channels only. */
43
+ export declare function maybeTunnel(workspaceDir: string, routeChannels: string[], boundPort: number, tunnel: boolean, stateRoot?: string): void;
44
+ /**
45
+ * Load and start the workspace's `schedules/` — a time-trigger firing the agent on each cron. Starts iff
46
+ * there are static schedules OR `selfSchedule` is on. Best-effort stop on process signals.
47
+ */
48
+ export declare function startSchedules(workspaceDir: string, agent: Agent, stateRoot: string, selfSchedule: boolean): Promise<void>;