@fastagent-sh/fastagent 0.28.0 → 0.30.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 +10 -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 +3 -1
- package/dist/channels/feishu/feishu-api.js +4 -0
- package/dist/channels/feishu/feishu.d.ts +0 -3
- package/dist/channels/feishu/feishu.js +10 -10
- package/dist/channels/feishu/register-app.d.ts +2 -0
- package/dist/channels/feishu/register-app.js +4 -6
- 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/wait-health.js +1 -1
- 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 -310
- 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 -67
- 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 +63 -24
- 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.d.ts +12 -0
- package/dist/cli/commands/mcp.js +164 -0
- 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/program.js +339 -108
- 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.js +50 -6
- 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 +26 -2
- 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 -11
- 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 +80 -39
- 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 +18 -43
- package/dist/deploy/docker/run.js +57 -79
- 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 -22
- package/dist/deploy/fly/run.js +25 -44
- 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 -7
- 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.d.ts +3 -0
- package/dist/harnesses/pi-durable/mcp.js +32 -0
- package/dist/harnesses/pi-durable/open.d.ts +36 -38
- package/dist/harnesses/pi-durable/open.js +97 -150
- 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 +95 -15
- package/dist/harnesses/pi-durable/serve.js +151 -59
- 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/server.d.ts +1 -1
- package/dist/http/server.js +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +8 -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/locked-file.d.ts +2 -2
- package/dist/locked-file.js +8 -3
- package/dist/mcp/auth.d.ts +22 -0
- package/dist/mcp/auth.js +180 -0
- package/dist/mcp/client.d.ts +15 -0
- package/dist/mcp/client.js +179 -0
- package/dist/mcp/config.d.ts +86 -0
- package/dist/mcp/config.js +225 -0
- package/dist/mcp/grants.d.ts +4 -0
- package/dist/mcp/grants.js +132 -0
- package/dist/mcp/host.d.ts +19 -0
- package/dist/mcp/host.js +128 -0
- package/dist/mcp/network.d.ts +12 -0
- package/dist/mcp/network.js +150 -0
- package/dist/mcp/sign-in.d.ts +2 -0
- package/dist/mcp/sign-in.js +204 -0
- package/dist/mcp-api.d.ts +107 -0
- package/dist/mcp-api.js +10 -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/setup-page.d.ts +51 -0
- package/dist/setup-page.js +149 -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 +12 -6
- package/dist/value-file.js +36 -25
- package/dist/version.d.ts +7 -1
- package/dist/version.js +9 -3
- package/package.json +8 -6
- package/dist/channels/lark/onboard.d.ts +0 -36
- package/dist/channels/lark/onboard.js +0 -56
- 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 -201
- package/dist/cli/add-feishu.d.ts +0 -50
- package/dist/cli/add-feishu.js +0 -367
- 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
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fastagent channels setup <name>`: set up one channel's platform app for one instance (channel-setup.md §2): ask a
|
|
3
|
+
* person for the values only they have, check them with the platform, and keep them in the instance's value file.
|
|
4
|
+
* With `--record-url`: record, in the configuration a deploy delivers, the URL a person set on a Slack or Lark app's
|
|
5
|
+
* page, once the deployment confirms the platform reaches it there (§5).
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { join, resolve } from "node:path";
|
|
9
|
+
import { recordChannelUrl } from "../channel-app.js";
|
|
10
|
+
import { isSetupKind, setUpChannel } from "../channel-setup.js";
|
|
11
|
+
import { webhookPath } from "../../channels/webhooks.js";
|
|
12
|
+
import { enterAgentEnv } from "../../env.js";
|
|
13
|
+
import { readInstanceConfigFile, resolveConfig } from "../../harnesses/pi-durable/config.js";
|
|
14
|
+
import { chosenIngress } from "../../harnesses/pi-durable/definition.js";
|
|
15
|
+
import { buildDefinitionShape, loadSourceDefinition } from "../../harnesses/pi-durable/open.js";
|
|
16
|
+
import { setChannelConfig } from "../../authoring.js";
|
|
17
|
+
import { INSTANCE_CONFIG_FILE, selectAgentEnvironment } from "../../paths.js";
|
|
18
|
+
import { markDevServe, unmarkDevServe } from "../../serving-command.js";
|
|
19
|
+
import { dotEnvPath, loadEnvValues } from "../../value-file.js";
|
|
20
|
+
import { agentDirOrExit, failStartup, failUsage } from "../fail.js";
|
|
21
|
+
import { enterEnvFile } from "../instance.js";
|
|
22
|
+
import { isInteractive, shellArg } from "../shared.js";
|
|
23
|
+
import { checkTicket, printReport, refusedCheck, runTicket, startTicket, ticketExitCode, ticketReport, } from "../ticket.js";
|
|
24
|
+
import { stripTrailingSlashes } from "../../strip-edges.js";
|
|
25
|
+
/** Without a terminal, how long a setup stays open for the person to finish their part. */
|
|
26
|
+
const UNATTENDED_TIMEOUT_SECONDS = 900;
|
|
27
|
+
export async function runChannelsSetup(name, dirArg, opts) {
|
|
28
|
+
if (opts.check !== undefined)
|
|
29
|
+
return checkSetup(name, opts.check, opts);
|
|
30
|
+
if (opts.recordUrl)
|
|
31
|
+
return recordUrl(name, dirArg, opts);
|
|
32
|
+
if (!isSetupKind(name)) {
|
|
33
|
+
failUsage(`${name}: \`channels setup\` sets up telegram, slack, feishu and lark`);
|
|
34
|
+
}
|
|
35
|
+
if (opts.ticket !== undefined) {
|
|
36
|
+
// The process `startTicket` started: it takes the ticket before anything slow (the definition is loaded to plan),
|
|
37
|
+
// then does the whole setup, its person the ticket's.
|
|
38
|
+
await runTicket(opts.ticket, async (step) => ({
|
|
39
|
+
...(await carryOut(await planSetup(name, dirArg, opts), step.person)),
|
|
40
|
+
}));
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
const plan = await planSetup(name, dirArg, opts);
|
|
44
|
+
if (opts.input === false || opts.json || !isInteractive())
|
|
45
|
+
return setUpUnattended(plan, opts);
|
|
46
|
+
const outcome = await carryOut(plan).catch(failStartup);
|
|
47
|
+
console.error(" next steps:");
|
|
48
|
+
for (const line of outcome.next_steps)
|
|
49
|
+
console.error(` ${line}`);
|
|
50
|
+
// Platform sockets left open would hold a one-shot command open after everything durable is done.
|
|
51
|
+
process.exit(0);
|
|
52
|
+
}
|
|
53
|
+
async function planSetup(kind, dirArg, opts) {
|
|
54
|
+
if (opts.ingress !== undefined && opts.ingress !== "webhook" && opts.ingress !== "connection") {
|
|
55
|
+
failUsage(`--ingress must be "webhook" or "connection", got "${opts.ingress}"`);
|
|
56
|
+
}
|
|
57
|
+
const agentDir = agentDirOrExit(resolve(dirArg));
|
|
58
|
+
// The development instance is the default one; naming a configuration or a value file is naming another.
|
|
59
|
+
const dev = opts.config === undefined && opts.envFile === undefined;
|
|
60
|
+
selectAgentEnvironment(dev ? "dev" : "production");
|
|
61
|
+
if (dev)
|
|
62
|
+
markDevServe();
|
|
63
|
+
else
|
|
64
|
+
unmarkDevServe();
|
|
65
|
+
enterEnvFile(opts.envFile);
|
|
66
|
+
enterAgentEnv(agentDir);
|
|
67
|
+
const file = dotEnvPath(agentDir);
|
|
68
|
+
const config = opts.config !== undefined ? resolve(opts.config) : dev ? join(agentDir, INSTANCE_CONFIG_FILE) : undefined;
|
|
69
|
+
if (opts.ingress !== undefined && config === undefined) {
|
|
70
|
+
failUsage("--ingress writes into a configuration: pass --config <file>, the one `deploy --config` delivers");
|
|
71
|
+
}
|
|
72
|
+
const definition = await loadSourceDefinition(agentDir).catch(failStartup);
|
|
73
|
+
const instance = config !== undefined && existsSync(config) ? await readInstanceConfigFile(config).catch(failStartup) : undefined;
|
|
74
|
+
const effective = resolveConfig(definition, instance);
|
|
75
|
+
const way = opts.ingress ?? chosenIngress(effective, kind, dev);
|
|
76
|
+
// A channel file that names its factory receives one way whatever the configuration says: setting the app up for
|
|
77
|
+
// the other would change nothing but the app.
|
|
78
|
+
if (existsSync(join(agentDir, "src", "channels", `${kind}.ts`))) {
|
|
79
|
+
try {
|
|
80
|
+
buildDefinitionShape(definition, process.env, {
|
|
81
|
+
settings: definition.config.defaults?.settings ?? {},
|
|
82
|
+
channels: { [kind]: { ingress: way } },
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
failStartup(new Error(`the agent does not build for ${kind} to receive by ${way} (${error.message})`));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const publicUrl = effective.http?.publicUrl;
|
|
90
|
+
return {
|
|
91
|
+
kind,
|
|
92
|
+
agentDir,
|
|
93
|
+
dev,
|
|
94
|
+
file,
|
|
95
|
+
way,
|
|
96
|
+
publicUrl,
|
|
97
|
+
writeIngress: opts.ingress === undefined ? undefined : config,
|
|
98
|
+
rerun: `fastagent channels setup ${kind} ${shellArg(agentDir)}` +
|
|
99
|
+
`${opts.ingress === undefined ? "" : ` --ingress ${opts.ingress}`}` +
|
|
100
|
+
`${opts.config === undefined ? "" : ` --config ${shellArg(resolve(opts.config))}`}` +
|
|
101
|
+
`${opts.envFile === undefined ? "" : ` --env-file ${shellArg(resolve(opts.envFile))}`}`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** The setup itself, once its person's part can be done: the values, the way written down, and what comes next. */
|
|
105
|
+
async function carryOut(plan, person) {
|
|
106
|
+
const outcome = await setUpChannel({
|
|
107
|
+
kind: plan.kind,
|
|
108
|
+
agentDir: plan.agentDir,
|
|
109
|
+
file: plan.file,
|
|
110
|
+
way: plan.way,
|
|
111
|
+
dev: plan.dev,
|
|
112
|
+
rerun: plan.rerun,
|
|
113
|
+
...(plan.publicUrl === undefined ? {} : { publicUrl: plan.publicUrl }),
|
|
114
|
+
...(person === undefined ? {} : { person }),
|
|
115
|
+
});
|
|
116
|
+
if (plan.writeIngress !== undefined) {
|
|
117
|
+
await setChannelConfig(plan.agentDir, plan.kind, { ingress: plan.way }, { config: plan.writeIngress });
|
|
118
|
+
console.error(`[fastagent] wrote channels.${plan.kind}.ingress "${plan.way}" to ${plan.writeIngress}`);
|
|
119
|
+
}
|
|
120
|
+
const serve = plan.dev
|
|
121
|
+
? "fastagent dev"
|
|
122
|
+
: `fastagent start --env-file ${shellArg(plan.file)} # or, from .secrets/production/.env, fastagent deploy <host> --run`;
|
|
123
|
+
return { written: outcome.written, kept: outcome.kept, next_steps: [serve, ...outcome.after] };
|
|
124
|
+
}
|
|
125
|
+
/** The command that reads a setup's ticket, as every such ticket records it. */
|
|
126
|
+
const setupCheck = (kind) => `fastagent channels setup ${kind} --check`;
|
|
127
|
+
/**
|
|
128
|
+
* A setup with no one at a terminal to ask, for a coding agent: it runs in a process of its own (ticket.ts), and the
|
|
129
|
+
* command returns once the person has a step to take, it is done, or it is still working at it. What only the person
|
|
130
|
+
* has is pasted on a page of the setup's own, never asked for in the agent's conversation and never typed into a file.
|
|
131
|
+
*/
|
|
132
|
+
async function setUpUnattended(plan, opts) {
|
|
133
|
+
const timeout = opts.timeout === undefined ? UNATTENDED_TIMEOUT_SECONDS : Number(opts.timeout);
|
|
134
|
+
if (!Number.isInteger(timeout) || timeout <= 0)
|
|
135
|
+
failUsage("--timeout must be a whole number of seconds");
|
|
136
|
+
const ticket = await startTicket({
|
|
137
|
+
// One setup per agent, channel and value file: another one's is its own business.
|
|
138
|
+
subject: `channels-setup:${plan.kind}:${plan.file}`,
|
|
139
|
+
action: `set up the ${plan.kind} app`,
|
|
140
|
+
check: setupCheck(plan.kind),
|
|
141
|
+
restart: plan.rerun,
|
|
142
|
+
timeoutSeconds: timeout,
|
|
143
|
+
waitsFirst: false,
|
|
144
|
+
whenRunning: "replace",
|
|
145
|
+
command: {
|
|
146
|
+
file: process.execPath,
|
|
147
|
+
args: [
|
|
148
|
+
...process.execArgv,
|
|
149
|
+
process.argv[1],
|
|
150
|
+
"channels",
|
|
151
|
+
"setup",
|
|
152
|
+
plan.kind,
|
|
153
|
+
plan.agentDir,
|
|
154
|
+
...(opts.ingress === undefined ? [] : ["--ingress", opts.ingress]),
|
|
155
|
+
...(opts.config === undefined ? [] : ["--config", resolve(opts.config)]),
|
|
156
|
+
...(opts.envFile === undefined ? [] : ["--env-file", resolve(opts.envFile)]),
|
|
157
|
+
],
|
|
158
|
+
cwd: process.cwd(),
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
reportSetup(ticket, opts, "started");
|
|
162
|
+
process.exit(ticketExitCode(ticket));
|
|
163
|
+
}
|
|
164
|
+
/** `channels setup <name> --check <ticket>`. */
|
|
165
|
+
async function checkSetup(name, id, opts) {
|
|
166
|
+
const refused = refusedCheck(id, setupCheck(name));
|
|
167
|
+
if (refused !== undefined)
|
|
168
|
+
failUsage(refused);
|
|
169
|
+
// There: refusedCheck read it, and a ticket is pruned only a day after it ends.
|
|
170
|
+
const ticket = (await checkTicket(id));
|
|
171
|
+
reportSetup(ticket, opts, "checked");
|
|
172
|
+
process.exit(ticketExitCode(ticket));
|
|
173
|
+
}
|
|
174
|
+
/** A setup's ticket for whoever started or checked it: the shared report, and what a finished setup adds. */
|
|
175
|
+
function reportSetup(ticket, opts, when) {
|
|
176
|
+
const { json, text } = ticketReport(ticket, when, (result) => {
|
|
177
|
+
const written = result.written ?? [];
|
|
178
|
+
const kept = result.kept ?? [];
|
|
179
|
+
const next = result.next_steps ?? [];
|
|
180
|
+
return {
|
|
181
|
+
fields: { written, kept, next_steps: next },
|
|
182
|
+
text: [...(written.length > 0 ? [`wrote ${written.join(", ")}`] : []), ...next.map((line) => `next: ${line}`)],
|
|
183
|
+
next: "The app is set up. Its next steps are in next_steps.",
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
printReport(opts.json, json, text);
|
|
187
|
+
}
|
|
188
|
+
async function recordUrl(name, dirArg, opts) {
|
|
189
|
+
if (name !== "slack" && name !== "lark") {
|
|
190
|
+
failUsage(`${name}: only slack and lark have a URL a person sets (the instance points the others itself)`);
|
|
191
|
+
}
|
|
192
|
+
if (opts.config === undefined)
|
|
193
|
+
failUsage("--record-url writes into the configuration a deploy delivers: pass --config <file>");
|
|
194
|
+
const agentDir = agentDirOrExit(resolve(dirArg));
|
|
195
|
+
selectAgentEnvironment("production");
|
|
196
|
+
enterEnvFile(opts.envFile);
|
|
197
|
+
// The deployment's /health and the platform are reached as the instance's values and environment say, a proxy included.
|
|
198
|
+
enterAgentEnv(agentDir);
|
|
199
|
+
const config = resolve(opts.config);
|
|
200
|
+
const source = await readInstanceConfigFile(config).catch(failStartup);
|
|
201
|
+
const named = source.values.http?.publicUrl;
|
|
202
|
+
const configured = typeof named === "string" ? stripTrailingSlashes(named) : undefined;
|
|
203
|
+
// `--url` is where this machine reads the deployment's /health; the URL the box confirms is the configuration's
|
|
204
|
+
// own http.publicUrl when it names one, the one the platforms reach.
|
|
205
|
+
const reach = (opts.url === undefined ? undefined : stripTrailingSlashes(opts.url)) ?? configured;
|
|
206
|
+
const base = configured ?? reach;
|
|
207
|
+
if (reach === undefined || base === undefined) {
|
|
208
|
+
failUsage(`${config} names no http.publicUrl: pass --url <the deployment's public URL>`);
|
|
209
|
+
}
|
|
210
|
+
const kind = name;
|
|
211
|
+
const url = `${base}${webhookPath(kind)}`;
|
|
212
|
+
const outcome = await healthOutcome(`${reach}/health`, kind).catch(failStartup);
|
|
213
|
+
if (outcome !== "registered") {
|
|
214
|
+
failStartup(new Error(`${kind}: the deployment at ${reach} reports it ${outcome ?? "not listed"}, not reached at ${url} yet — save ` +
|
|
215
|
+
`${url} on the app's page (the platform verifies it with the deployment), then run this again`));
|
|
216
|
+
}
|
|
217
|
+
await recordChannelUrl(agentDir, config, kind, url, loadEnvValues(dotEnvPath(agentDir))).catch(failStartup);
|
|
218
|
+
printReport(opts.json, { status: "done", channel: kind, url, config }, [`recorded ${url} for ${kind} in ${config}`]);
|
|
219
|
+
}
|
|
220
|
+
/** The outcome the deployment's `/health` reports for `kind`, or undefined when it lists none. */
|
|
221
|
+
async function healthOutcome(healthUrl, kind) {
|
|
222
|
+
let body;
|
|
223
|
+
try {
|
|
224
|
+
body = (await (await fetch(healthUrl, { signal: AbortSignal.timeout(10_000) })).json());
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
throw new Error(`could not read ${healthUrl}: ${String(error)}`, { cause: error });
|
|
228
|
+
}
|
|
229
|
+
return body.webhooks?.find((entry) => entry.channel === kind)?.outcome;
|
|
230
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type SourceOptions } from "../../content/source.ts";
|
|
2
|
+
import { type InstanceFlags } from "../instance.ts";
|
|
2
3
|
/** A name refusal is the caller's to fix by naming another (exit 2); anything else is a startup failure (exit 1). */
|
|
3
4
|
export declare function failEdit(hint: string): (error: unknown) => never;
|
|
4
|
-
export declare function runContentList(dirArg: string, json: boolean): Promise<void>;
|
|
5
|
-
export declare function runContentAdd(source: string, dirArg: string, opts: SourceOptions
|
|
6
|
-
|
|
5
|
+
export declare function runContentList(dirArg: string, json: boolean, flags?: InstanceFlags): Promise<void>;
|
|
6
|
+
export declare function runContentAdd(source: string, dirArg: string, opts: SourceOptions & {
|
|
7
|
+
program?: boolean;
|
|
8
|
+
instance?: InstanceFlags;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export declare function runContentRemove(name: string, dirArg: string, flags?: InstanceFlags): Promise<void>;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `fastagent content list|add|remove`: what the agent works on, as `context.json` declares it and
|
|
3
|
-
*
|
|
2
|
+
* `fastagent content list|add|remove`: what the agent works on, as the program's `context.json` declares it and the
|
|
3
|
+
* instance's configuration adds to it.
|
|
4
4
|
*/
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
import { ContentNameError, addContent, listContent, removeContent } from "../../authoring.js";
|
|
7
7
|
import { readContentSource } from "../../content/source.js";
|
|
8
8
|
import { contentLines } from "../content-view.js";
|
|
9
9
|
import { failStartup, failUsage } from "../fail.js";
|
|
10
|
+
import { enterContentScope } from "../instance.js";
|
|
10
11
|
/** A name refusal is the caller's to fix by naming another (exit 2); anything else is a startup failure (exit 1). */
|
|
11
12
|
export function failEdit(hint) {
|
|
12
13
|
return (error) => {
|
|
@@ -15,30 +16,35 @@ export function failEdit(hint) {
|
|
|
15
16
|
failStartup(error);
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
|
-
export async function runContentList(dirArg, json) {
|
|
19
|
-
const
|
|
19
|
+
export async function runContentList(dirArg, json, flags) {
|
|
20
|
+
const dir = resolve(dirArg);
|
|
21
|
+
const { content } = await listContent(dir, enterContentScope(dir, flags)).catch(failStartup);
|
|
20
22
|
if (json) {
|
|
21
|
-
console.log(JSON.stringify({ content
|
|
23
|
+
console.log(JSON.stringify({ content }, null, 2));
|
|
22
24
|
return;
|
|
23
25
|
}
|
|
24
|
-
for (const [label, value] of contentLines(content
|
|
26
|
+
for (const [label, value] of contentLines(content))
|
|
25
27
|
console.log(`${`${label}:`.padEnd(10)} ${value}`);
|
|
26
28
|
}
|
|
27
29
|
export async function runContentAdd(source, dirArg, opts) {
|
|
30
|
+
const dir = resolve(dirArg);
|
|
31
|
+
const { program, instance, ...options } = opts;
|
|
28
32
|
const { addition, notes } = await Promise.resolve()
|
|
29
|
-
.then(() => readContentSource(source, process.cwd(),
|
|
33
|
+
.then(() => readContentSource(source, process.cwd(), options))
|
|
30
34
|
.catch(failStartup);
|
|
31
35
|
for (const note of notes)
|
|
32
36
|
console.error(`[fastagent] ${note}`);
|
|
33
|
-
const
|
|
37
|
+
const scope = enterContentScope(dir, instance);
|
|
38
|
+
const { name, content, notes: left, } = await addContent(dir, { ...addition, ...(program ? { program } : {}) }, scope).catch(failEdit(" — pass --name"));
|
|
34
39
|
const added = content.filter((entry) => entry.name === name);
|
|
35
40
|
for (const [label, value] of contentLines(added))
|
|
36
41
|
console.error(`[fastagent] ${label} ${value}`);
|
|
37
42
|
for (const note of left)
|
|
38
43
|
console.error(`[fastagent] ${note}`);
|
|
39
44
|
}
|
|
40
|
-
export async function runContentRemove(name, dirArg) {
|
|
41
|
-
const
|
|
45
|
+
export async function runContentRemove(name, dirArg, flags) {
|
|
46
|
+
const dir = resolve(dirArg);
|
|
47
|
+
const removed = await removeContent(dir, name, enterContentScope(dir, flags)).catch(failEdit(""));
|
|
42
48
|
console.error(`[fastagent] removed content ${removed.name}`);
|
|
43
49
|
for (const note of removed.notes)
|
|
44
50
|
console.error(`[fastagent] ${note}`);
|
|
@@ -11,10 +11,11 @@ import { awsCli, awsJson } from "../../../deploy/agentcore/aws-cli.js";
|
|
|
11
11
|
import { agentcoreShell } from "../../../deploy/agentcore/shell.js";
|
|
12
12
|
import { awsRunner, spawnRunner } from "../../../deploy/runner.js";
|
|
13
13
|
import { SECRET_FILE_MODE, exists } from "../../../paths.js";
|
|
14
|
-
import {
|
|
14
|
+
import { deploySecrets } from "./secrets-step.js";
|
|
15
15
|
import { failStartup } from "../../fail.js";
|
|
16
16
|
import { shellArg } from "../../shared.js";
|
|
17
|
-
import {
|
|
17
|
+
import { ownPublicUrl } from "../../../deploy/container.js";
|
|
18
|
+
import { boxLoginStep, registrarsFor, urlRecorder } from "./shared.js";
|
|
18
19
|
export const agentcoreHost = {
|
|
19
20
|
isOurs: (path, content) => path.endsWith(TEMPLATE_FILE) && isGeneratedAgentcoreTemplate(content),
|
|
20
21
|
async shell(agentDir) {
|
|
@@ -55,7 +56,7 @@ export const agentcoreHost = {
|
|
|
55
56
|
console.error(`[fastagent] note: AgentCore resets the runtime's storage on every deploy AND after 14 idle days, and the ` +
|
|
56
57
|
`${boxLogin} login with it: every deploy of this agent ends with a login on the runtime, and after an idle ` +
|
|
57
58
|
`reset every turn fails until \`fastagent login ${boxLogin} --deployment agentcore\` (the runtime's log says ` +
|
|
58
|
-
`so). For an agent that must keep answering unattended, set ${boxLogin}'s API key
|
|
59
|
+
`so). For an agent that must keep answering unattended, set ${boxLogin}'s API key with \`fastagent secrets set agentcore <its variable>\`.`);
|
|
59
60
|
}
|
|
60
61
|
// Schedules and wake-ups need nothing from the template beyond the forwarder every stack has: the container
|
|
61
62
|
// mirrors them into EventBridge schedules itself (schedule/wake-alarm.ts). A schedule that does not build is the
|
|
@@ -98,9 +99,13 @@ export const agentcoreHost = {
|
|
|
98
99
|
rerun: ctx.rerun,
|
|
99
100
|
channels,
|
|
100
101
|
declaredSecrets,
|
|
102
|
+
modelKey: pre.modelKey,
|
|
103
|
+
api: ctx.api,
|
|
101
104
|
values,
|
|
102
105
|
valueFile,
|
|
103
106
|
topology: plan.topology,
|
|
107
|
+
publicUrl: ownPublicUrl(container.config),
|
|
108
|
+
config: opts.config,
|
|
104
109
|
});
|
|
105
110
|
return undefined;
|
|
106
111
|
}
|
|
@@ -111,24 +116,23 @@ export const agentcoreHost = {
|
|
|
111
116
|
/** `deploy agentcore --run`: drive aws + docker to completion. */
|
|
112
117
|
async function runDeployAgentcore(params) {
|
|
113
118
|
const { agentDir, name, channels, topology } = params;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
`${params.boxLogin}'s API key in ${params.valueFile}`));
|
|
122
|
-
}
|
|
123
|
-
const { secrets, missingSecrets } = assembleSecrets({
|
|
119
|
+
const secretsStep = deploySecrets({
|
|
120
|
+
host: "agentcore",
|
|
121
|
+
label: "AWS Secrets Manager",
|
|
122
|
+
agentDir,
|
|
123
|
+
opts: { config: params.config },
|
|
124
|
+
answerer: params.answerer,
|
|
125
|
+
api: params.api,
|
|
124
126
|
modelAuth: params.modelAuth,
|
|
125
|
-
|
|
127
|
+
modelKey: params.modelKey,
|
|
128
|
+
declaredSecrets: params.declaredSecrets,
|
|
129
|
+
valueFile: params.valueFile,
|
|
126
130
|
values: params.values,
|
|
127
131
|
});
|
|
128
|
-
// The
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
// The model's key on the target is a credential like a key in the value file: the runtime has it from its first
|
|
133
|
+
// invocation, so there is no login to run on the box. Only the target can say, hence after the secrets step.
|
|
134
|
+
let keyOnTarget = false;
|
|
135
|
+
const login = boxLoginStep("agentcore", params, (runtimeArn) => agentcoreShell(runtimeArn, ingressSessionId(name), awsRunner(agentDir))).boxLogin;
|
|
132
136
|
// The params temp dir holds the ONE file carrying secret values (file:// parameter-overrides — never argv);
|
|
133
137
|
// 0700/0600 and removed after the run, success or gate.
|
|
134
138
|
const paramsDir = await mkdtemp(join(tmpdir(), "fastagent-agentcore-"));
|
|
@@ -142,12 +146,40 @@ async function runDeployAgentcore(params) {
|
|
|
142
146
|
.replace(/[-:.TZ]/g, "")
|
|
143
147
|
.slice(0, 14),
|
|
144
148
|
region: process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
provideSecrets: async (store) => {
|
|
150
|
+
const gate = await secretsStep(store);
|
|
151
|
+
if (gate !== undefined)
|
|
152
|
+
return gate;
|
|
153
|
+
const held = await store.names();
|
|
154
|
+
keyOnTarget = params.modelKey.variables.some((variable) => held.has(variable));
|
|
155
|
+
// Decided before the build and the stack: the deploy resets the runtime's storage and the login with it, so
|
|
156
|
+
// with nobody to log it in again it would end at "not logged in" AFTER replacing a runtime that was serving,
|
|
157
|
+
// with the webhooks a previous deploy registered still pointing at it. A coding agent's ticket has someone:
|
|
158
|
+
// its user.
|
|
159
|
+
if (params.boxLogin && !keyOnTarget && params.answerer.kind === "nobody") {
|
|
160
|
+
return (`every AgentCore deploy wipes the runtime's ${params.boxLogin} login, and without a terminal ` +
|
|
161
|
+
`nobody can log it in again — run this deploy in a terminal; from a coding agent, run ` +
|
|
162
|
+
`\`${params.rerun} --json\`, which hands the login to its user; or set ` +
|
|
163
|
+
`${params.boxLogin}'s API key with \`fastagent secrets set agentcore ${params.modelKey.variables[0] ?? "<its variable>"}\``);
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
},
|
|
167
|
+
// The wake-alarm shared secret (container ↔ forwarder).
|
|
168
|
+
wakeSecret: crypto.randomUUID(),
|
|
169
|
+
// The forwarder→runtime ingress secret: what makes an envelope the forwarder's rather than any IAM principal's.
|
|
170
|
+
ingressSecret: crypto.randomUUID(),
|
|
148
171
|
channels,
|
|
149
172
|
topology,
|
|
150
|
-
...
|
|
173
|
+
...(params.publicUrl === undefined ? {} : { publicUrl: params.publicUrl }),
|
|
174
|
+
...urlRecorder(agentDir, params.config, params.values, params.answerer),
|
|
175
|
+
...(login === undefined
|
|
176
|
+
? {}
|
|
177
|
+
: {
|
|
178
|
+
boxLogin: {
|
|
179
|
+
...login,
|
|
180
|
+
run: async (runtimeArn) => (keyOnTarget ? undefined : login.run(runtimeArn)),
|
|
181
|
+
},
|
|
182
|
+
}),
|
|
151
183
|
}, awsRunner(agentDir), spawnRunner("docker", agentDir), (m) => console.error(`[fastagent] ${m}`), async (content) => {
|
|
152
184
|
const path = join(paramsDir, "params.json");
|
|
153
185
|
await writeFile(path, content, { mode: SECRET_FILE_MODE });
|
|
@@ -156,7 +188,7 @@ async function runDeployAgentcore(params) {
|
|
|
156
188
|
const path = join(paramsDir, "forwarder.zip");
|
|
157
189
|
await writeFile(path, bytes);
|
|
158
190
|
return path;
|
|
159
|
-
}, registrarsFor(
|
|
191
|
+
}, registrarsFor(params.values, params.answerer));
|
|
160
192
|
if (!outcome.ok)
|
|
161
193
|
failStartup(new Error(`deploy stopped: ${outcome.gate}`));
|
|
162
194
|
console.error(`[fastagent] deployed → ${outcome.runtimeArn}`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The production Feishu or Lark app a deploy sets up on the spot when the target's values hold none: the same
|
|
3
|
+
* `channels setup` a person runs by hand, aimed at the production value file (channel-setup.md §6).
|
|
4
|
+
*/
|
|
5
|
+
import type { DeclaredChannel } from "../../../channels/discover.ts";
|
|
6
|
+
import type { SetupWay } from "../../../channels/setup.ts";
|
|
7
|
+
import type { Person } from "../../person.ts";
|
|
8
|
+
export interface ChannelAppSetup {
|
|
9
|
+
kind: "feishu" | "lark";
|
|
10
|
+
way: SetupWay;
|
|
11
|
+
}
|
|
12
|
+
/** The names of the values `app`'s setup would keep. */
|
|
13
|
+
export declare const channelAppNames: ({ kind, way }: ChannelAppSetup) => string[];
|
|
14
|
+
/** The Feishu and Lark channels whose app has no value yet in `values`. */
|
|
15
|
+
export declare function unpreparedChannelApps(channels: readonly DeclaredChannel[], values: ReadonlyMap<string, string>): ChannelAppSetup[];
|
|
16
|
+
/** Set each of `apps` up for production, its person's steps taken by `person` (absent: at this terminal). */
|
|
17
|
+
export declare function prepareChannelApps(agentDir: string, apps: readonly ChannelAppSetup[], rerun: string, person?: Person): Promise<void>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { dotEnvPath } from "../../../value-file.js";
|
|
2
|
+
import { setUpChannel, setupNames } from "../../channel-setup.js";
|
|
3
|
+
/** The names of the values `app`'s setup would keep. */
|
|
4
|
+
export const channelAppNames = ({ kind, way }) => setupNames(kind, way);
|
|
5
|
+
/** The Feishu and Lark channels whose app has no value yet in `values`. */
|
|
6
|
+
export function unpreparedChannelApps(channels, values) {
|
|
7
|
+
return ["feishu", "lark"].flatMap((kind) => {
|
|
8
|
+
const channel = channels.find((entry) => entry.name === kind);
|
|
9
|
+
if (!channel)
|
|
10
|
+
return [];
|
|
11
|
+
const way = channel.ingress === "webhook" ? "webhook" : "connection";
|
|
12
|
+
return setupNames(kind, way).some((name) => !values.get(name)?.trim()) ? [{ kind, way }] : [];
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/** Set each of `apps` up for production, its person's steps taken by `person` (absent: at this terminal). */
|
|
16
|
+
export async function prepareChannelApps(agentDir, apps, rerun, person) {
|
|
17
|
+
const file = dotEnvPath(agentDir);
|
|
18
|
+
for (const { kind, way } of apps) {
|
|
19
|
+
console.error(`[fastagent] ${kind}: preparing the production app for ${way} in ${file}`);
|
|
20
|
+
const input = { kind: kind, agentDir, file, way, dev: false, rerun };
|
|
21
|
+
const { after } = await setUpChannel(person === undefined ? input : { ...input, person });
|
|
22
|
+
// What is left (the publish, a scope to approve) is not the deploy's to finish: the person is told.
|
|
23
|
+
for (const line of after) {
|
|
24
|
+
if (person === undefined)
|
|
25
|
+
console.error(`[fastagent] next: ${line}`);
|
|
26
|
+
else
|
|
27
|
+
person.afterwards(line);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|