@fastagent-sh/fastagent 0.29.0 → 0.30.1
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 +9 -4
- package/dist/atomic-write.d.ts +4 -5
- package/dist/atomic-write.js +4 -5
- package/dist/authoring.d.ts +32 -15
- package/dist/authoring.js +147 -127
- package/dist/channels/agentcore-protocol.d.ts +8 -3
- package/dist/channels/agentcore-protocol.js +3 -1
- package/dist/channels/agentcore-service.d.ts +1 -0
- package/dist/channels/agentcore-service.js +14 -4
- package/dist/channels/agentcore.d.ts +9 -0
- package/dist/channels/agentcore.js +29 -4
- package/dist/channels/context.d.ts +2 -0
- package/dist/channels/context.js +5 -1
- package/dist/channels/feishu/feishu-api.d.ts +2 -1
- package/dist/channels/feishu/feishu.d.ts +0 -3
- package/dist/channels/feishu/feishu.js +10 -10
- package/dist/channels/feishu/register-app.js +3 -5
- package/dist/channels/feishu/scaffold/channel.ts +32 -34
- package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
- package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
- package/dist/channels/feishu/setup-mode.d.ts +2 -12
- package/dist/channels/feishu/setup-mode.js +2 -22
- package/dist/channels/feishu/setup.d.ts +12 -0
- package/dist/channels/feishu/setup.js +197 -0
- package/dist/channels/kit/abortable.d.ts +8 -0
- package/dist/channels/kit/abortable.js +22 -0
- package/dist/channels/lark/lark.d.ts +0 -3
- package/dist/channels/lark/lark.js +0 -5
- package/dist/channels/lark/scaffold/channel.ts +33 -35
- package/dist/channels/lark/scaffold/lark-send.ts +5 -5
- package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
- package/dist/channels/lark/setup.d.ts +9 -0
- package/dist/channels/lark/setup.js +57 -0
- package/dist/channels/setup.d.ts +112 -0
- package/dist/channels/setup.js +31 -0
- package/dist/channels/slack/history.js +1 -1
- package/dist/channels/slack/manifest.d.ts +17 -7
- package/dist/channels/slack/manifest.js +12 -16
- package/dist/channels/slack/scaffold/channel.ts +44 -30
- package/dist/channels/slack/scaffold/slack-send.ts +8 -8
- package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
- package/dist/channels/slack/setup.d.ts +7 -0
- package/dist/channels/slack/setup.js +119 -0
- package/dist/channels/slack/slack-api.d.ts +2 -0
- package/dist/channels/slack/slack-api.js +4 -0
- package/dist/channels/slack/slack.d.ts +21 -2
- package/dist/channels/slack/slack.js +398 -338
- package/dist/channels/slack/socket.d.ts +57 -0
- package/dist/channels/slack/socket.js +305 -0
- package/dist/channels/telegram/polling.d.ts +27 -0
- package/dist/channels/telegram/polling.js +105 -0
- package/dist/channels/telegram/scaffold/channel.ts +52 -32
- package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
- package/dist/channels/telegram/setup.d.ts +2 -0
- package/dist/channels/telegram/setup.js +42 -0
- package/dist/channels/telegram/telegram-api.d.ts +27 -1
- package/dist/channels/telegram/telegram-api.js +7 -6
- package/dist/channels/telegram/telegram.d.ts +11 -3
- package/dist/channels/telegram/telegram.js +184 -157
- package/dist/channels/webhook-evidence.d.ts +11 -0
- package/dist/channels/webhook-evidence.js +71 -0
- package/dist/channels/webhooks.d.ts +37 -0
- package/dist/channels/webhooks.js +70 -0
- package/dist/child-env.d.ts +16 -0
- package/dist/child-env.js +30 -0
- package/dist/cli/box-login.js +4 -1
- package/dist/cli/channel-app.d.ts +3 -0
- package/dist/cli/channel-app.js +35 -0
- package/dist/cli/channel-setup.d.ts +38 -0
- package/dist/cli/channel-setup.js +195 -0
- package/dist/cli/commands/add.d.ts +6 -14
- package/dist/cli/commands/add.js +45 -303
- package/dist/cli/commands/auth.js +8 -2
- package/dist/cli/commands/build.d.ts +5 -0
- package/dist/cli/commands/build.js +16 -0
- package/dist/cli/commands/channels.d.ts +19 -0
- package/dist/cli/commands/channels.js +230 -0
- package/dist/cli/commands/content.d.ts +7 -3
- package/dist/cli/commands/content.js +16 -10
- package/dist/cli/commands/deploy/agentcore.js +55 -23
- package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
- package/dist/cli/commands/deploy/channel-apps.js +30 -0
- package/dist/cli/commands/deploy/docker.js +32 -65
- package/dist/cli/commands/deploy/fly.js +28 -12
- package/dist/cli/commands/deploy/railway.js +38 -13
- package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
- package/dist/cli/commands/deploy/secrets-step.js +66 -0
- package/dist/cli/commands/deploy/shared.d.ts +16 -5
- package/dist/cli/commands/deploy/shared.js +25 -8
- package/dist/cli/commands/deploy.js +53 -60
- package/dist/cli/commands/dev.d.ts +2 -0
- package/dist/cli/commands/dev.js +15 -3
- package/dist/cli/commands/env.js +1 -1
- package/dist/cli/commands/info.d.ts +2 -0
- package/dist/cli/commands/info.js +60 -25
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/invoke.d.ts +2 -0
- package/dist/cli/commands/invoke.js +20 -7
- package/dist/cli/commands/login.d.ts +0 -7
- package/dist/cli/commands/login.js +22 -10
- package/dist/cli/commands/logout.js +1 -1
- package/dist/cli/commands/mcp.js +14 -6
- package/dist/cli/commands/models.js +3 -2
- package/dist/cli/commands/schedules.d.ts +4 -2
- package/dist/cli/commands/schedules.js +14 -10
- package/dist/cli/commands/secrets.d.ts +14 -0
- package/dist/cli/commands/secrets.js +213 -0
- package/dist/cli/commands/start.d.ts +14 -14
- package/dist/cli/commands/start.js +46 -60
- package/dist/cli/content-view.d.ts +4 -6
- package/dist/cli/content-view.js +5 -11
- package/dist/cli/first-run-channels.d.ts +3 -0
- package/dist/cli/first-run-channels.js +63 -0
- package/dist/cli/instance.d.ts +41 -0
- package/dist/cli/instance.js +155 -0
- package/dist/cli/invoke-stream.d.ts +5 -2
- package/dist/cli/invoke-stream.js +7 -2
- package/dist/cli/kernel.js +3 -0
- package/dist/cli/program.js +282 -110
- package/dist/cli/serve.d.ts +4 -5
- package/dist/cli/serve.js +4 -7
- package/dist/cli/shared.d.ts +11 -2
- package/dist/cli/shared.js +32 -19
- package/dist/cli/ticket.d.ts +7 -0
- package/dist/cli/ticket.js +106 -30
- package/dist/content/declare.d.ts +31 -37
- package/dist/content/declare.js +59 -60
- package/dist/content/file.d.ts +23 -12
- package/dist/content/file.js +48 -35
- package/dist/content/git.d.ts +0 -10
- package/dist/content/git.js +5 -39
- package/dist/content/resolve.d.ts +18 -22
- package/dist/content/resolve.js +32 -66
- package/dist/content/source.d.ts +7 -8
- package/dist/content/source.js +10 -28
- package/dist/credentials-page.d.ts +26 -0
- package/dist/credentials-page.js +185 -0
- package/dist/declared-secrets.d.ts +10 -3
- package/dist/declared-secrets.js +8 -4
- package/dist/deploy/agentcore/destroy.d.ts +4 -1
- package/dist/deploy/agentcore/destroy.js +25 -1
- package/dist/deploy/agentcore/forwarder.js +1 -1
- package/dist/deploy/agentcore/plan.d.ts +7 -21
- package/dist/deploy/agentcore/plan.js +47 -49
- package/dist/deploy/agentcore/run.d.ts +18 -10
- package/dist/deploy/agentcore/run.js +93 -70
- package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
- package/dist/deploy/agentcore/runtime-secrets.js +108 -0
- package/dist/deploy/agentcore/shell.js +12 -19
- package/dist/deploy/agentcore/sigv4.d.ts +7 -0
- package/dist/deploy/agentcore/sigv4.js +15 -0
- package/dist/deploy/agentcore/slr.d.ts +18 -0
- package/dist/deploy/agentcore/slr.js +27 -0
- package/dist/deploy/agentcore/store.d.ts +7 -0
- package/dist/deploy/agentcore/store.js +115 -0
- package/dist/deploy/api.d.ts +14 -14
- package/dist/deploy/api.js +26 -24
- package/dist/deploy/box-shell.d.ts +3 -1
- package/dist/deploy/build-context.d.ts +4 -4
- package/dist/deploy/build-context.js +11 -15
- package/dist/deploy/channel-ingress.d.ts +35 -9
- package/dist/deploy/channel-ingress.js +119 -22
- package/dist/deploy/container.d.ts +35 -10
- package/dist/deploy/container.js +79 -37
- package/dist/deploy/delivered-text.d.ts +13 -0
- package/dist/deploy/delivered-text.js +86 -0
- package/dist/deploy/docker/plan.d.ts +7 -8
- package/dist/deploy/docker/plan.js +31 -52
- package/dist/deploy/docker/run.d.ts +5 -43
- package/dist/deploy/docker/run.js +9 -84
- package/dist/deploy/file-store.d.ts +2 -0
- package/dist/deploy/file-store.js +17 -0
- package/dist/deploy/fly/plan.d.ts +3 -2
- package/dist/deploy/fly/plan.js +20 -9
- package/dist/deploy/fly/run.d.ts +18 -8
- package/dist/deploy/fly/run.js +24 -42
- package/dist/deploy/fly/store.d.ts +12 -0
- package/dist/deploy/fly/store.js +83 -0
- package/dist/deploy/hosts.d.ts +1 -2
- package/dist/deploy/hosts.js +1 -2
- package/dist/deploy/preflight.d.ts +18 -5
- package/dist/deploy/preflight.js +135 -63
- package/dist/deploy/railway/ignore.d.ts +8 -0
- package/dist/deploy/railway/ignore.js +57 -0
- package/dist/deploy/railway/plan.d.ts +1 -2
- package/dist/deploy/railway/plan.js +8 -6
- package/dist/deploy/railway/run.d.ts +21 -15
- package/dist/deploy/railway/run.js +32 -78
- package/dist/deploy/railway/store.d.ts +29 -0
- package/dist/deploy/railway/store.js +143 -0
- package/dist/deploy/runner.d.ts +5 -2
- package/dist/deploy/runner.js +4 -3
- package/dist/deploy/secret-store.d.ts +63 -0
- package/dist/deploy/secret-store.js +98 -0
- package/dist/deploy/secrets.d.ts +29 -46
- package/dist/deploy/secrets.js +49 -122
- package/dist/deploy/workspace.d.ts +6 -12
- package/dist/deploy/workspace.js +18 -94
- package/dist/dev-supervisor.js +6 -8
- package/dist/environment/declare.js +3 -2
- package/dist/environment/mise.d.ts +3 -1
- package/dist/environment/mise.js +4 -2
- package/dist/feishu.d.ts +1 -1
- package/dist/feishu.js +1 -1
- package/dist/harnesses/models/catalog.d.ts +1 -0
- package/dist/harnesses/models/catalog.js +1 -2
- package/dist/harnesses/models/environment.d.ts +7 -2
- package/dist/harnesses/models/environment.js +9 -5
- package/dist/harnesses/models/host.d.ts +0 -7
- package/dist/harnesses/models/host.js +10 -1
- package/dist/harnesses/models/login.d.ts +0 -8
- package/dist/harnesses/models/login.js +5 -4
- package/dist/harnesses/models/models.d.ts +7 -2
- package/dist/harnesses/models/models.js +28 -4
- package/dist/harnesses/pi-durable/agent.js +59 -38
- package/dist/harnesses/pi-durable/build.d.ts +12 -0
- package/dist/harnesses/pi-durable/build.js +270 -0
- package/dist/harnesses/pi-durable/built.d.ts +24 -0
- package/dist/harnesses/pi-durable/built.js +136 -0
- package/dist/harnesses/pi-durable/config.d.ts +81 -0
- package/dist/harnesses/pi-durable/config.js +266 -0
- package/dist/harnesses/pi-durable/define.d.ts +63 -13
- package/dist/harnesses/pi-durable/define.js +23 -2
- package/dist/harnesses/pi-durable/definition.d.ts +72 -0
- package/dist/harnesses/pi-durable/definition.js +143 -0
- package/dist/harnesses/pi-durable/events.d.ts +4 -3
- package/dist/harnesses/pi-durable/events.js +179 -136
- package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
- package/dist/harnesses/pi-durable/execution-env.js +11 -0
- package/dist/harnesses/pi-durable/mcp.js +1 -3
- package/dist/harnesses/pi-durable/open.d.ts +35 -37
- package/dist/harnesses/pi-durable/open.js +91 -153
- package/dist/harnesses/pi-durable/reads.d.ts +0 -1
- package/dist/harnesses/pi-durable/reads.js +1 -3
- package/dist/harnesses/pi-durable/registry.d.ts +4 -6
- package/dist/harnesses/pi-durable/registry.js +29 -26
- package/dist/harnesses/pi-durable/release.d.ts +2 -2
- package/dist/harnesses/pi-durable/release.js +10 -7
- package/dist/harnesses/pi-durable/serve.d.ts +91 -15
- package/dist/harnesses/pi-durable/serve.js +109 -47
- package/dist/harnesses/pi-durable/sessions.js +15 -12
- package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
- package/dist/harnesses/pi-durable/text-sources.js +55 -0
- package/dist/harnesses/pi-durable/text.d.ts +16 -4
- package/dist/harnesses/pi-durable/text.js +115 -59
- package/dist/harnesses/pi-durable/wake.js +8 -8
- package/dist/http/client.js +9 -3
- package/dist/http/contract.d.ts +3 -3
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +4 -1
- package/dist/instance-name.d.ts +7 -0
- package/dist/instance-name.js +26 -0
- package/dist/lark.d.ts +1 -1
- package/dist/lark.js +1 -1
- package/dist/mcp/client.js +11 -2
- package/dist/mcp/config.d.ts +70 -18
- package/dist/mcp/config.js +136 -53
- package/dist/mcp/host.d.ts +5 -2
- package/dist/mcp/host.js +14 -6
- package/dist/mcp/sign-in.js +2 -2
- package/dist/mcp-api.d.ts +14 -0
- package/dist/paths.d.ts +22 -8
- package/dist/paths.js +44 -11
- package/dist/runtime-websocket.d.ts +14 -0
- package/dist/runtime-websocket.js +39 -0
- package/dist/scaffold/add-channel.d.ts +9 -16
- package/dist/scaffold/add-channel.js +13 -83
- package/dist/scaffold/templates/env.example +4 -5
- package/dist/scaffold/templates/fastagent.config.ts +20 -14
- package/dist/scaffold/templates/gitignore +9 -6
- package/dist/scaffold/templates.d.ts +6 -2
- package/dist/scaffold/templates.js +7 -2
- package/dist/scaffold/vendor-skill.js +4 -2
- package/dist/service.d.ts +44 -6
- package/dist/service.js +110 -10
- package/dist/serving-command.d.ts +10 -0
- package/dist/serving-command.js +1 -0
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/dist/strip-edges.d.ts +10 -0
- package/dist/strip-edges.js +22 -0
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +1 -1
- package/dist/tunnel.d.ts +0 -1
- package/dist/tunnel.js +2 -8
- package/dist/value-file.d.ts +11 -6
- package/dist/value-file.js +21 -9
- package/dist/version.d.ts +7 -1
- package/dist/version.js +9 -3
- package/package.json +6 -5
- package/dist/channels/lark/credentials-page.d.ts +0 -25
- package/dist/channels/lark/credentials-page.js +0 -206
- package/dist/channels/lark/onboard.d.ts +0 -52
- package/dist/channels/lark/onboard.js +0 -86
- package/dist/channels/slack/config-api.d.ts +0 -69
- package/dist/channels/slack/config-api.js +0 -162
- package/dist/channels/slack/onboard.d.ts +0 -40
- package/dist/channels/slack/onboard.js +0 -136
- package/dist/channels/slack/onboarding-state.d.ts +0 -31
- package/dist/channels/slack/onboarding-state.js +0 -64
- package/dist/channels/slack/register-webhook.d.ts +0 -12
- package/dist/channels/slack/register-webhook.js +0 -67
- package/dist/channels/slack/setup-server.d.ts +0 -32
- package/dist/channels/slack/setup-server.js +0 -246
- package/dist/cli/add-feishu.d.ts +0 -41
- package/dist/cli/add-feishu.js +0 -391
- package/dist/cli/add-slack.d.ts +0 -18
- package/dist/cli/add-slack.js +0 -216
- package/dist/harnesses/pi-durable/parked.d.ts +0 -18
- package/dist/harnesses/pi-durable/parked.js +0 -27
package/dist/cli/commands/add.js
CHANGED
|
@@ -1,318 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `fastagent add <channel>|skill` — scaffold channel glue (`src/channels/<kind>.ts`) or vendor an Agent Skills skill.
|
|
3
|
-
*
|
|
3
|
+
* A channel changes the program only (channel-setup.md §2): its platform app, and the values of the instance that runs
|
|
4
|
+
* it, are `channels setup`'s.
|
|
4
5
|
*/
|
|
5
|
-
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { join, relative, resolve } from "node:path";
|
|
7
|
-
import {
|
|
8
|
-
import { terminalPerson } from "../person.js";
|
|
9
|
-
import { isInteractive, shellArg } from "../shared.js";
|
|
10
|
-
import { checkTicket, printReport, refusedCheck, runTicket, startTicket, ticketExitCode, ticketReport, } from "../ticket.js";
|
|
11
|
-
import { cloudFor } from "../../channels/feishu/cloud.js";
|
|
12
|
-
import { feishuIngressFor } from "../../channels/feishu/setup-mode.js";
|
|
13
|
-
import { markDevServe, unmarkDevServe } from "../../serving-command.js";
|
|
14
|
-
import { channelIngress } from "../../channels/discover.js";
|
|
15
|
-
import { buildDefinitionShape, prepareDefinition } from "../../harnesses/pi-durable/open.js";
|
|
7
|
+
import { shellArg } from "../shared.js";
|
|
16
8
|
import { enterAgentEnv } from "../../env.js";
|
|
17
|
-
import {
|
|
18
|
-
import { selectAgentEnvironment, resolveStateRoot, SECRETS_DIRNAME, isUnderDir, displayPath } from "../../paths.js";
|
|
9
|
+
import { SECRETS_DIRNAME, displayPath } from "../../paths.js";
|
|
19
10
|
import { detectRuntime, readPackageJson } from "../../runtime.js";
|
|
20
|
-
import {
|
|
11
|
+
import { appendChannelEnv, assertChannelReady, channelExists, channelSetup, scaffoldChannel, scaffoldCompanionTools, } from "../../scaffold/add-channel.js";
|
|
21
12
|
import { vendorSkill } from "../../scaffold/vendor-skill.js";
|
|
22
|
-
import { failStartup, failUsage
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return checkSetup(channelKind, opts.check, opts);
|
|
30
|
-
if (opts.ticket !== undefined) {
|
|
31
|
-
// The process `startTicket` started: it takes the ticket before anything slow (the definition is built to plan),
|
|
32
|
-
// then does the whole `add`, its person the ticket's.
|
|
33
|
-
await runTicket(opts.ticket, async (step) => ({
|
|
34
|
-
...(await addChannel(channelKind, await planAdd(channelKind, dirArg, opts), opts, step.person)),
|
|
35
|
-
}));
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
const plan = await planAdd(channelKind, dirArg, opts);
|
|
39
|
-
const unattended = opts.input === false || opts.json === true || !isInteractive();
|
|
40
|
-
if (unattended && opts.onboard !== false && channelKind !== "telegram") {
|
|
41
|
-
return setUpUnattended(channelKind, opts, plan);
|
|
42
|
-
}
|
|
43
|
-
const added = await addChannel(channelKind, plan, opts, terminalPerson).catch(failStartup);
|
|
44
|
-
if (opts.json)
|
|
45
|
-
console.log(JSON.stringify({ status: "done", ...added }, null, 2));
|
|
46
|
-
// App-creation flows leave platform/tunnel sockets behind that would otherwise hold the one-shot scaffold command
|
|
47
|
-
// open after all durable boundaries have completed.
|
|
48
|
-
process.exit(0);
|
|
49
|
-
}
|
|
50
|
-
/** What `add` settles before it changes anything, refusing what it can while nothing has changed. */
|
|
51
|
-
async function planAdd(channelKind, dirArg, opts) {
|
|
52
|
-
// The channel (glue + companion tools + secrets) is agent surface: everything lands in the AGENT DIR.
|
|
53
|
-
if (opts.replaceConfig && opts.onboard === false) {
|
|
54
|
-
failUsage("--replace-config replaces onboarding credentials; it cannot be combined with --no-onboard");
|
|
55
|
-
}
|
|
13
|
+
import { agentDirOrExit, failStartup, failUsage } from "../fail.js";
|
|
14
|
+
/**
|
|
15
|
+
* `add <channel>`: the program only (channel-setup.md §2). It writes the channel file and its tools and names the
|
|
16
|
+
* values they read in `.secrets/.env.example`; it asks nothing and creates no app. The app, and the values of the
|
|
17
|
+
* instance that runs it, are `channels setup`'s.
|
|
18
|
+
*/
|
|
19
|
+
export async function runAddChannel(kind, dirArg, opts) {
|
|
56
20
|
const target = agentDirOrExit(resolve(dirArg));
|
|
57
|
-
const environment = opts.environment ?? "dev";
|
|
58
|
-
if (environment !== "dev" && environment !== "production") {
|
|
59
|
-
failUsage(`--env must be "dev" or "production", got "${environment}"`);
|
|
60
|
-
}
|
|
61
|
-
selectAgentEnvironment(environment);
|
|
62
|
-
if (environment === "production")
|
|
63
|
-
unmarkDevServe();
|
|
64
|
-
else
|
|
65
|
-
markDevServe();
|
|
66
|
-
enterAgentEnv(target);
|
|
67
|
-
if (environment === "production" && (channelKind === "feishu" || channelKind === "lark")) {
|
|
68
|
-
try {
|
|
69
|
-
assertIndependentFeishuApps(target, [channelKind], loadEnvValues(dotEnvPath(target)));
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
failStartup(error);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Paths are printed to someone standing in their CWD, which may be elsewhere, while every file belongs to the AGENT
|
|
76
|
-
// dir — prefix them, or they point at nothing.
|
|
77
21
|
const agentFromCwd = displayPath(process.cwd(), target);
|
|
78
22
|
const inAgent = (p) => (agentFromCwd === undefined ? p : join(agentFromCwd, p));
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Everything `add <kind>` does once the person's part can be done: the channel, its tools and variables, the app's
|
|
103
|
-
* onboarding through `person`, and the next steps, printed and returned with the variables it wrote.
|
|
104
|
-
*/
|
|
105
|
-
async function addChannel(channelKind, plan, opts, person) {
|
|
106
|
-
const { target, environment, inAgent, agentFromCwd, file, existsAlready, ingress, pinned, setting, rerun } = plan;
|
|
107
|
-
const envPath = dotEnvPath(target);
|
|
108
|
-
const envLabel = isUnderDir(envPath, target) ? inAgent(relative(target, envPath)) : envPath;
|
|
109
|
-
const before = loadEnvValues(envPath);
|
|
110
|
-
if (existsAlready) {
|
|
111
|
-
console.error(`[fastagent] ${inAgent(relative(target, file))} already exists — keeping it`);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
await assertChannelReady(target);
|
|
115
|
-
await scaffoldChannel(target, channelKind);
|
|
116
|
-
console.error(`[fastagent] created ${inAgent(relative(target, file))}`);
|
|
117
|
-
}
|
|
118
|
-
// Companion tools are the package's, not authored glue.
|
|
119
|
-
for (const tool of await scaffoldCompanionTools(target, channelKind)) {
|
|
120
|
-
console.error(`[fastagent] wrote ${inAgent(relative(target, tool))}`);
|
|
121
|
-
}
|
|
122
|
-
if (await appendChannelEnv(target, channelKind)) {
|
|
123
|
-
console.error(`[fastagent] added ${channelKind} env vars to ${inAgent(join(SECRETS_DIRNAME, ".env.example"))}`);
|
|
124
|
-
}
|
|
125
|
-
// Stateful app onboarding is re-runnable after the scaffold boundary.
|
|
126
|
-
let created;
|
|
127
|
-
if (channelKind === "slack" && opts.onboard !== false) {
|
|
128
|
-
const { onboardSlackInternalApp } = await import("../add-slack.js");
|
|
129
|
-
created = await onboardSlackInternalApp({
|
|
130
|
-
target,
|
|
131
|
-
stateRoot: resolveStateRoot(target),
|
|
132
|
-
replaceConfig: opts.replaceConfig,
|
|
133
|
-
...(person === terminalPerson ? {} : { person }),
|
|
134
|
-
}).then(() => undefined);
|
|
135
|
-
}
|
|
136
|
-
else if ((channelKind === "feishu" || channelKind === "lark") && opts.onboard !== false) {
|
|
137
|
-
created = await onboardFeishuCloudApp(target, channelKind, ingress, rerun, person);
|
|
138
|
-
}
|
|
139
|
-
const setup = channelSetup(channelKind, ingress, pinned);
|
|
140
|
-
const env = setup.env;
|
|
141
|
-
const steps = channelKind === "slack" && opts.onboard !== false
|
|
142
|
-
? [
|
|
143
|
-
// No line about the Request URL: the `dev --tunnel` line below is the whole instruction, and FastAgent sets
|
|
144
|
-
// that URL itself when the agent first runs.
|
|
145
|
-
"invite the app to each channel it should read",
|
|
146
|
-
// The tool lines are the scaffold's (one list), whichever path set the app up.
|
|
147
|
-
...setup.steps.filter((step) => step.includes("{tools}/")),
|
|
148
|
-
]
|
|
149
|
-
: setup.steps;
|
|
150
|
-
const generated = Object.fromEntries(env.filter((e) => e.generate).map((e) => [e.name, randomBytes(24).toString("hex")]));
|
|
151
|
-
// Kind-neutral: every channel's generated secrets get the same treatment.
|
|
152
|
-
const dotEnv = await appendChannelDotEnv(target, channelKind, { ...generated, ...created, ...setting }, [...Object.keys(created ?? {}), ...Object.keys(setting ?? {})], ingress);
|
|
153
|
-
if (dotEnv.unprotectedSecretsDir) {
|
|
154
|
-
console.error(`[fastagent] note: ${unprotectedSecretsNote(dotEnv.unprotectedSecretsDir)}`);
|
|
155
|
-
}
|
|
156
|
-
if (dotEnv.written.length > 0) {
|
|
157
|
-
console.error(`[fastagent] wrote ${dotEnv.written.join(", ")} to ${envLabel}`);
|
|
158
|
-
}
|
|
23
|
+
const file = join(target, "src", "channels", `${kind}.ts`);
|
|
24
|
+
const created = [];
|
|
25
|
+
await (async () => {
|
|
26
|
+
if (await channelExists(target, kind)) {
|
|
27
|
+
console.error(`[fastagent] ${inAgent(relative(target, file))} already exists — keeping it`);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
await assertChannelReady(target);
|
|
31
|
+
await scaffoldChannel(target, kind);
|
|
32
|
+
created.push(inAgent(relative(target, file)));
|
|
33
|
+
console.error(`[fastagent] created ${inAgent(relative(target, file))}`);
|
|
34
|
+
}
|
|
35
|
+
// Companion tools are the package's, not authored glue.
|
|
36
|
+
for (const tool of await scaffoldCompanionTools(target, kind)) {
|
|
37
|
+
created.push(inAgent(relative(target, tool)));
|
|
38
|
+
console.error(`[fastagent] wrote ${inAgent(relative(target, tool))}`);
|
|
39
|
+
}
|
|
40
|
+
if (await appendChannelEnv(target, kind)) {
|
|
41
|
+
console.error(`[fastagent] added ${kind} env vars to ${inAgent(join(SECRETS_DIRNAME, ".env.example"))}`);
|
|
42
|
+
}
|
|
43
|
+
})().catch(failStartup);
|
|
159
44
|
const install = detectRuntime(target, await readPackageJson(target)).runtime === "bun" ? "bun install" : "npm install";
|
|
160
|
-
const next = [
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
45
|
+
const next = [
|
|
46
|
+
`${agentFromCwd === undefined ? install : `(cd ${agentFromCwd} && ${install})`} # if @fastagent-sh/fastagent is not installed yet`,
|
|
47
|
+
`fastagent channels setup ${kind}${agentFromCwd === undefined ? "" : ` ${shellArg(agentFromCwd)}`} # creates the app and asks for its credentials`,
|
|
48
|
+
`edit ${inAgent(relative(target, file))} — customise routing with route() (optional; the defaults already work)`,
|
|
49
|
+
...channelSetup(kind).steps.map((line) => line.replace("{tools}", inAgent(join("src", "tools")))),
|
|
50
|
+
"fastagent dev",
|
|
51
|
+
];
|
|
52
|
+
console.error(" next steps:");
|
|
53
|
+
for (const line of next)
|
|
164
54
|
console.error(` ${line}`);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// Onboarding writes some itself (an app's ID and secret, a bot token) before the write above: what this `add` wrote is
|
|
169
|
-
// what the file holds now and did not before.
|
|
170
|
-
const now = loadEnvValues(envPath);
|
|
171
|
-
const written = [...now.keys()].filter((name) => now.get(name) && now.get(name) !== before.get(name));
|
|
172
|
-
for (const e of env) {
|
|
173
|
-
if (!written.includes(e.name) && dotEnv.alreadySet.includes(e.name))
|
|
174
|
-
continue; // the user's already: nothing to do
|
|
175
|
-
if (written.includes(e.name)) {
|
|
176
|
-
// Written, but its hint may still carry an action — keep the variable visible instead of silently absorbing it.
|
|
177
|
-
step(`${e.name} — ${e.generate ? "generated and " : ""}written to ${envLabel} # ${e.hint}`);
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
const value = e.generate ? `=${generated[e.name]}` : "";
|
|
181
|
-
const action = e.required ? "set" : "optionally set";
|
|
182
|
-
step(`${action} ${e.name}${value} in ${envLabel} # ${e.hint}`);
|
|
183
|
-
}
|
|
184
|
-
const serveCommand = environment === "production" ? "fastagent start" : "fastagent dev";
|
|
185
|
-
for (const s of steps) {
|
|
186
|
-
step(s
|
|
187
|
-
.replace("{channel}", inAgent(relative(target, file)))
|
|
188
|
-
.replace("{tools}", inAgent(join("src", "tools")))
|
|
189
|
-
.replaceAll("{serve}", serveCommand));
|
|
190
|
-
}
|
|
191
|
-
if (environment === "production") {
|
|
192
|
-
step(`fastagent deploy <host> --run # deploy with the production app and credentials`);
|
|
193
|
-
}
|
|
194
|
-
else if (ingress === "websocket") {
|
|
195
|
-
step(`fastagent dev # no public URL or tunnel required`);
|
|
196
|
-
}
|
|
197
|
-
else if (channelKind === "slack") {
|
|
198
|
-
step(`fastagent dev --tunnel # ${opts.onboard === false ? "serve locally + print the URL to paste into the Slack app's Event Subscriptions" : "start the agent — then message it in Slack"}`);
|
|
199
|
-
}
|
|
200
|
-
else if (channelKind !== "lark") {
|
|
201
|
-
step(`fastagent dev --tunnel # serve locally + a public URL, auto-registering the webhook`);
|
|
202
|
-
}
|
|
203
|
-
return { written, next_steps: next };
|
|
204
|
-
}
|
|
205
|
-
/** The command that reads an app's setup ticket, as every such ticket records it. */
|
|
206
|
-
const setupCheck = (kind) => `fastagent add ${kind} --check`;
|
|
207
|
-
/** What the person does for each kind's app, to follow "so they can". */
|
|
208
|
-
const SETUP_ACTIONS = {
|
|
209
|
-
slack: "set up the Slack app: paste a configuration token on the page, then click Allow in Slack",
|
|
210
|
-
feishu: "confirm the new Feishu app",
|
|
211
|
-
lark: "finish setting up the Lark app",
|
|
212
|
-
};
|
|
213
|
-
/**
|
|
214
|
-
* `add slack|feishu|lark` with no one at a terminal to ask, for a coding agent: the app's setup runs in a process of its
|
|
215
|
-
* own (ticket.ts), and the command returns once the person has a step to take, it is done, or it is still working at it.
|
|
216
|
-
* What only the person has (Slack's tokens, Lark's App ID and Secret) is pasted on a page of the setup's own, never asked
|
|
217
|
-
* for in the agent's conversation and never typed into a file.
|
|
218
|
-
*/
|
|
219
|
-
async function setUpUnattended(kind, opts, at) {
|
|
220
|
-
const { target } = at;
|
|
221
|
-
const timeout = opts.timeout === undefined ? UNATTENDED_TIMEOUT_SECONDS : Number(opts.timeout);
|
|
222
|
-
if (!Number.isInteger(timeout) || timeout <= 0)
|
|
223
|
-
failUsage(`--timeout must be a whole number of seconds`);
|
|
224
|
-
const ticket = await startTicket({
|
|
225
|
-
// One setup per agent, channel and environment: another agent's is its own business.
|
|
226
|
-
subject: `add:${kind}:${at.environment}:${target}`,
|
|
227
|
-
action: SETUP_ACTIONS[kind],
|
|
228
|
-
check: setupCheck(kind),
|
|
229
|
-
restart: at.rerun,
|
|
230
|
-
timeoutSeconds: timeout,
|
|
231
|
-
waitsFirst: false,
|
|
232
|
-
whenRunning: "replace",
|
|
233
|
-
command: {
|
|
234
|
-
file: process.execPath,
|
|
235
|
-
args: [
|
|
236
|
-
...process.execArgv,
|
|
237
|
-
process.argv[1],
|
|
238
|
-
"add",
|
|
239
|
-
kind,
|
|
240
|
-
target,
|
|
241
|
-
...(at.environment === "production" ? ["--env", "production"] : []),
|
|
242
|
-
...(opts.ingress === undefined ? [] : ["--ingress", opts.ingress]),
|
|
243
|
-
...(opts.replaceConfig ? ["--replace-config"] : []),
|
|
244
|
-
],
|
|
245
|
-
cwd: process.cwd(),
|
|
246
|
-
},
|
|
247
|
-
});
|
|
248
|
-
reportSetup(ticket, opts, "started");
|
|
249
|
-
process.exit(ticketExitCode(ticket));
|
|
250
|
-
}
|
|
251
|
-
/** `add <kind> --check <ticket>`. */
|
|
252
|
-
async function checkSetup(kind, id, opts) {
|
|
253
|
-
const refused = refusedCheck(id, setupCheck(kind));
|
|
254
|
-
if (refused !== undefined)
|
|
255
|
-
failUsage(refused);
|
|
256
|
-
// There: refusedCheck read it, and a ticket is pruned only a day after it ends.
|
|
257
|
-
const ticket = (await checkTicket(id));
|
|
258
|
-
reportSetup(ticket, opts, "checked");
|
|
259
|
-
process.exit(ticketExitCode(ticket));
|
|
260
|
-
}
|
|
261
|
-
/** An app's setup ticket for whoever started or checked it: the shared report, and what a finished setup adds. */
|
|
262
|
-
function reportSetup(ticket, opts, when) {
|
|
263
|
-
const { json, text } = ticketReport(ticket, when, (result) => {
|
|
264
|
-
const written = result.written ?? [];
|
|
265
|
-
const next = result.next_steps ?? [];
|
|
266
|
-
return {
|
|
267
|
-
fields: { written, next_steps: next },
|
|
268
|
-
text: [...(written.length > 0 ? [`wrote ${written.join(", ")}`] : []), ...next.map((line) => `next: ${line}`)],
|
|
269
|
-
next: `The app is set up. Its next steps are in next_steps.`,
|
|
270
|
-
};
|
|
271
|
-
});
|
|
272
|
-
printReport(opts.json, json, text);
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* What `add` sets the app up for, and whether it writes the choice down. `--ingress` is the `<PREFIX>_INGRESS`
|
|
276
|
-
* setting in the selected environment. The channel rule supplies its default: WebSocket in dev, webhook in production.
|
|
277
|
-
*/
|
|
278
|
-
function resolveIngress(kind, raw) {
|
|
279
|
-
if (raw !== undefined && raw !== "webhook" && raw !== "websocket") {
|
|
280
|
-
failUsage(`--ingress must be "webhook" or "websocket", got "${raw}"`);
|
|
281
|
-
}
|
|
282
|
-
if (kind !== "feishu" && kind !== "lark")
|
|
283
|
-
return { ingress: "webhook", pinned: false };
|
|
284
|
-
const name = `${cloudFor(kind).envPrefix}_INGRESS`;
|
|
285
|
-
if (raw !== undefined)
|
|
286
|
-
return { ingress: raw, pinned: true, setting: { [name]: raw } };
|
|
287
|
-
try {
|
|
288
|
-
return {
|
|
289
|
-
ingress: feishuIngressFor(kind, process.env),
|
|
290
|
-
pinned: Boolean(process.env[name]?.trim()),
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
// A malformed value in the environment: the author's to fix, said as the rule says it.
|
|
295
|
-
return failUsage(error.message);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* A setting only takes effect in a channel file that reads it: one that names its factory receives the same way
|
|
300
|
-
* whatever the setting says, so writing the setting (and configuring the app for it) would change nothing but the app.
|
|
301
|
-
* Asked of the channel as the agent builds it, under the value this command is about to write.
|
|
302
|
-
*/
|
|
303
|
-
async function assertChannelReadsSetting(target, kind, setting, shown) {
|
|
304
|
-
Object.assign(process.env, setting); // the value this command writes anyway
|
|
305
|
-
const channel = await prepareDefinition(target)
|
|
306
|
-
.then((definition) => buildDefinitionShape(definition, process.env).channels.get(kind))
|
|
307
|
-
.catch((error) => failStartup(new Error(`the agent does not build (${error.message}) — fix it and re-run`)));
|
|
308
|
-
const [[name, value]] = Object.entries(setting);
|
|
309
|
-
const wanted = value === "webhook" ? "webhook" : "long-connection";
|
|
310
|
-
const shape = channel === undefined ? undefined : channelIngress(channel);
|
|
311
|
-
if (shape !== wanted) {
|
|
312
|
-
failStartup(new Error(`${shown} receives by ${shape === "webhook" ? "webhook" : "WebSocket"} whatever ${name} says — it names ` +
|
|
313
|
-
`its factory instead of reading the setting, so ${name}=${value} would change nothing but the app. Move ` +
|
|
314
|
-
`the file aside and re-run to scaffold one that reads it, or edit its factory yourself`));
|
|
315
|
-
}
|
|
55
|
+
if (opts.json)
|
|
56
|
+
console.log(JSON.stringify({ status: "done", created, next_steps: next }, null, 2));
|
|
57
|
+
process.exit(0);
|
|
316
58
|
}
|
|
317
59
|
/** `fastagent add skill <source> [dir]`: vendor an Agent Skills skill into <dir>/skills/<name>/. */
|
|
318
60
|
export async function runAddSkill(source, dirArg, opts) {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fastagent auth check`: whether a provider is ready for the agent here, as pi's `auth check` answers it, for a script
|
|
3
|
+
* or a coding agent: `ready`, `not_ready` or `invalid` on stdout, exit 0, 1 or 2. The answer is the one the agent's
|
|
4
|
+
* messages would get (`ModelEnvironment.authStatus`, a due refresh included).
|
|
5
|
+
*/
|
|
6
|
+
import { definedValues } from "../../declared-secrets.js";
|
|
1
7
|
import { enterAgentEnv } from "../../env.js";
|
|
2
8
|
import { modelEnvironment } from "../../harnesses/models/environment.js";
|
|
3
9
|
import { providerOf } from "../../harnesses/models/models.js";
|
|
@@ -13,12 +19,12 @@ export async function runAuthCheck(opts) {
|
|
|
13
19
|
let providers = [];
|
|
14
20
|
if (agentDir) {
|
|
15
21
|
enterAgentEnv(agentDir);
|
|
16
|
-
providers = await definitionProviders(agentDir).catch(failStartup);
|
|
22
|
+
providers = await definitionProviders(agentDir, process.env).catch(failStartup);
|
|
17
23
|
}
|
|
18
24
|
else {
|
|
19
25
|
installProxyFetch();
|
|
20
26
|
}
|
|
21
|
-
const environment = await modelEnvironment(agentDir, {}, providers);
|
|
27
|
+
const environment = await modelEnvironment(agentDir, {}, providers, { environment: definedValues(process.env) });
|
|
22
28
|
let provider = opts.provider;
|
|
23
29
|
let modelId;
|
|
24
30
|
if (opts.model !== undefined) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** `fastagent build [agent] [--no-check] [--json]`: the agent compiled into `dist/`. */
|
|
2
|
+
import { relative, resolve } from "node:path";
|
|
3
|
+
import { buildProject } from "../../harnesses/pi-durable/build.js";
|
|
4
|
+
import { log } from "../../log.js";
|
|
5
|
+
import { agentDirOrExit, failStartup } from "../fail.js";
|
|
6
|
+
export async function runBuild(dirArg, opts) {
|
|
7
|
+
const dir = agentDirOrExit(resolve(dirArg));
|
|
8
|
+
const { dist, definition, warnings } = await buildProject(dir, { check: opts.check }).catch(failStartup);
|
|
9
|
+
for (const warning of warnings)
|
|
10
|
+
log.warn(`[fastagent] ${warning}`);
|
|
11
|
+
if (opts.json) {
|
|
12
|
+
console.log(JSON.stringify({ dist, name: definition.name, version: definition.version, warnings }));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
console.log(relative(process.cwd(), dist) || dist);
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ChannelsSetupOptions {
|
|
2
|
+
recordUrl: boolean;
|
|
3
|
+
/** The instance configuration: what names how the channel receives, and where `--ingress` is written. */
|
|
4
|
+
config?: string;
|
|
5
|
+
/** The value file the credentials are kept in (created when missing). */
|
|
6
|
+
envFile?: string;
|
|
7
|
+
ingress?: string;
|
|
8
|
+
url?: string;
|
|
9
|
+
json: boolean;
|
|
10
|
+
/** false ⇔ `--no-input`. */
|
|
11
|
+
input?: boolean;
|
|
12
|
+
/** `--check <ticket>`: where a setup started without a terminal stands. */
|
|
13
|
+
check?: string;
|
|
14
|
+
/** `--timeout <seconds>`: how long a setup started without a terminal stays open. */
|
|
15
|
+
timeout?: string;
|
|
16
|
+
/** `--ticket <id>`: the process that runs a ticket's setup. */
|
|
17
|
+
ticket?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function runChannelsSetup(name: string, dirArg: string, opts: ChannelsSetupOptions): Promise<void>;
|