@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
|
@@ -2,21 +2,21 @@
|
|
|
2
2
|
* `fastagent deploy <host> [agent]`: generate host artifacts from the resolved definition and print an ordered deploy
|
|
3
3
|
* runbook.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { cloudFor } from "../../channels/feishu/cloud.js";
|
|
7
|
-
import { FEISHU_INGRESS_SETTINGS } from "../../channels/feishu/setup-mode.js";
|
|
8
|
-
import { assembleSecrets, divergentSettingsGate, missingValuesGate } from "../../deploy/secrets.js";
|
|
5
|
+
import { resolve } from "node:path";
|
|
9
6
|
import { dotEnvPath, loadEnvValues } from "../../value-file.js";
|
|
10
7
|
import { unmarkDevServe } from "../../serving-command.js";
|
|
11
|
-
import {
|
|
8
|
+
import { channelAppNames, prepareChannelApps, unpreparedChannelApps } from "./deploy/channel-apps.js";
|
|
12
9
|
import { displayPath, selectAgentEnvironment } from "../../paths.js";
|
|
10
|
+
import { buildProject } from "../../harnesses/pi-durable/build.js";
|
|
11
|
+
import { DELIVERED_CONFIG_DIR } from "../../deploy/container.js";
|
|
12
|
+
import { deliverText } from "../../deploy/delivered-text.js";
|
|
13
13
|
import { preflightDeploy } from "../../deploy/preflight.js";
|
|
14
|
-
import { apiReport, deploymentApi
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import { apiReport, deploymentApi } from "../../deploy/api.js";
|
|
15
|
+
import { API_KEY_VARIABLE, apiKeysFrom } from "../../http/server.js";
|
|
16
|
+
import { readInstanceConfigFile, resolveConfig } from "../../harnesses/pi-durable/config.js";
|
|
17
|
+
import { loadSourceDefinition } from "../../harnesses/pi-durable/open.js";
|
|
17
18
|
import { failStartup, failUsage } from "../fail.js";
|
|
18
19
|
import { enterAgentDirectory, isInteractive, shellArg } from "../shared.js";
|
|
19
|
-
import { terminalPerson } from "../person.js";
|
|
20
20
|
import { checkTicket, printReport, refusedCheck, runTicket, startTicket, stopTicket, ticketExitCode, ticketReport, } from "../ticket.js";
|
|
21
21
|
import { agentcoreHost } from "./deploy/agentcore.js";
|
|
22
22
|
import { dockerHost } from "./deploy/docker.js";
|
|
@@ -87,39 +87,33 @@ export async function runDeploy(host, dirArg, opts) {
|
|
|
87
87
|
async function deploy(host, dirArg, opts, step) {
|
|
88
88
|
if (opts.code !== undefined)
|
|
89
89
|
failUsage("--code hands a code to a deploy's ticket: pass --check <ticket> too");
|
|
90
|
-
if (opts.tunnel && host !== "docker") {
|
|
91
|
-
// A flag/host combination the parser cannot see (host is an argument) — usage class, exit 2.
|
|
92
|
-
failUsage(`deploy stopped: --tunnel is supported only by the local Docker target`);
|
|
93
|
-
}
|
|
94
90
|
if (opts.json && !opts.run)
|
|
95
91
|
failUsage("--json reports a deploy --run drives: pass --run too");
|
|
96
92
|
selectAgentEnvironment("production");
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
failStartup(error);
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
independentApps(loadEnvValues(dotEnvPath(agentDir)));
|
|
113
|
-
const api = deploymentApi({
|
|
114
|
-
config,
|
|
115
|
-
hostServes: host !== "agentcore",
|
|
116
|
-
values: loadEnvValues(dotEnvPath(agentDir)),
|
|
93
|
+
// The configuration the box runs on: the program's defaults, with the instance configuration `--config` delivers,
|
|
94
|
+
// and never the project's own `fastagent.json`, the development instance's.
|
|
95
|
+
const delivered = opts.config === undefined
|
|
96
|
+
? undefined
|
|
97
|
+
: await readInstanceConfigFile(resolve(opts.config))
|
|
98
|
+
.then((source) => ({ source, text: `${JSON.stringify(source.values, null, 2)}\n` }))
|
|
99
|
+
.catch(failStartup);
|
|
100
|
+
const deployed = async (dir) => ({
|
|
101
|
+
definition: await loadSourceDefinition(dir),
|
|
102
|
+
...(delivered === undefined ? {} : { instance: delivered.source }),
|
|
117
103
|
});
|
|
104
|
+
const { agentDir } = await enterAgentDirectory(dirArg, opts, undefined, deployed).catch(failStartup);
|
|
105
|
+
const config = await deployed(agentDir)
|
|
106
|
+
.then(({ definition }) => resolveConfig(definition, delivered?.source))
|
|
107
|
+
.catch(failStartup);
|
|
108
|
+
// Never `dev`, whatever spawned this: a channel's way of receiving is chosen as the box will choose it.
|
|
109
|
+
unmarkDevServe();
|
|
110
|
+
const api = deploymentApi({ config, hostServes: host !== "agentcore" });
|
|
118
111
|
const target = HOSTS[host];
|
|
119
112
|
const preflight = async () => {
|
|
120
113
|
const facts = await preflightDeploy({
|
|
121
114
|
agentDir,
|
|
122
115
|
config,
|
|
116
|
+
...(delivered === undefined ? {} : { delivered }),
|
|
123
117
|
run: !!opts.run,
|
|
124
118
|
force: !!opts.force,
|
|
125
119
|
noResidentProcess: host === "agentcore", // alarms and webhooks wake the container there — no machine to keep up
|
|
@@ -135,17 +129,10 @@ async function deploy(host, dirArg, opts, step) {
|
|
|
135
129
|
let pre = await preflight();
|
|
136
130
|
const agentArg = displayPath(process.cwd(), agentDir);
|
|
137
131
|
const rerun = `fastagent deploy ${host}${agentArg === undefined ? "" : ` ${shellArg(agentArg)}`}` +
|
|
138
|
-
`${opts.
|
|
139
|
-
const unprepared = opts.run ?
|
|
132
|
+
`${opts.intoLinked ? " --into-linked" : ""} --run`;
|
|
133
|
+
const unprepared = opts.run ? unpreparedChannelApps(pre.channels, pre.values) : [];
|
|
140
134
|
if (opts.json && step === undefined) {
|
|
141
135
|
// A coding agent's deploy: what this machine can refuse is refused above, at once; the rest runs in a ticket.
|
|
142
|
-
if (unprepared.some(({ kind }) => kind === "lark")) {
|
|
143
|
-
const needed = await larkCredentialsNeeded(agentDir, rerun).catch(failStartup);
|
|
144
|
-
if (needed !== undefined) {
|
|
145
|
-
printReport(true, needed.json, needed.text);
|
|
146
|
-
process.exit(0);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
136
|
const timeout = opts.timeout === undefined ? TICKET_TIMEOUT_SECONDS : Number(opts.timeout);
|
|
150
137
|
if (!Number.isInteger(timeout) || timeout <= 0)
|
|
151
138
|
failUsage("--timeout must be a whole number of seconds");
|
|
@@ -167,8 +154,8 @@ async function deploy(host, dirArg, opts, step) {
|
|
|
167
154
|
"deploy",
|
|
168
155
|
host,
|
|
169
156
|
agentDir,
|
|
170
|
-
...(opts.tunnel ? ["--tunnel"] : []),
|
|
171
157
|
...(opts.force ? ["--force"] : []),
|
|
158
|
+
...(delivered === undefined ? [] : ["--config", delivered.source.label]),
|
|
172
159
|
...(opts.intoLinked ? ["--into-linked"] : []),
|
|
173
160
|
"--run",
|
|
174
161
|
"--json",
|
|
@@ -182,50 +169,38 @@ async function deploy(host, dirArg, opts, step) {
|
|
|
182
169
|
/** Everything past the pre-flight, its person's steps taken by `answerer`; the URL and what the API report says. */
|
|
183
170
|
const deployWith = async (answerer) => {
|
|
184
171
|
if (unprepared.length > 0) {
|
|
185
|
-
const supplied = unprepared.flatMap(
|
|
172
|
+
const supplied = unprepared.flatMap(channelAppNames);
|
|
186
173
|
const kinds = unprepared.map(({ kind }) => kind);
|
|
187
174
|
const { values } = pre;
|
|
188
175
|
const absent = supplied.filter((name) => !values.get(name)?.trim());
|
|
189
|
-
|
|
190
|
-
modelAuth: pre.modelAuth,
|
|
191
|
-
declared: pre.declaredSecrets,
|
|
192
|
-
values: pre.values,
|
|
193
|
-
});
|
|
194
|
-
const missing = missingValuesGate(missingSecrets.filter((name) => !supplied.includes(name)), pre.valueFile);
|
|
195
|
-
if (missing)
|
|
196
|
-
failStartup(new Error(`deploy stopped: ${missing}`));
|
|
197
|
-
if (host === "docker" && !opts.tunnel && unprepared.some(({ ingress }) => ingress === "webhook")) {
|
|
176
|
+
if (host === "docker" && unprepared.some(({ way }) => way === "webhook")) {
|
|
198
177
|
failStartup(new Error(`deploy stopped: ${kinds.join(", ")} receive by webhook here, and this Docker deployment has no ` +
|
|
199
|
-
`public URL to point the app at —
|
|
200
|
-
`
|
|
201
|
-
|
|
178
|
+
`public URL to point the app at — set up your own ingress and supply ${absent.join(", ")} from a separate production app in ${pre.valueFile}, or ` +
|
|
179
|
+
`set ${kinds.map((kind) => `channels.${kind}.ingress`).join(", ")} to "connection" in the configuration ` +
|
|
180
|
+
`--config delivers`));
|
|
202
181
|
}
|
|
203
182
|
if (answerer.kind === "nobody") {
|
|
204
183
|
failUsage(`deploy stopped: ${pre.valueFile} has no ${absent.join(", ")}: setting up the separate production app ` +
|
|
205
184
|
`needs a person — run this deploy in a terminal; from a coding agent, run \`${rerun} --json\`, which ` +
|
|
206
185
|
`hands their part to its user`);
|
|
207
186
|
}
|
|
208
|
-
const person = answerer.kind === "ticket" ? answerer.step.person : terminalPerson;
|
|
209
187
|
const again = answerer.kind === "ticket" ? `${rerun} --json` : rerun;
|
|
210
|
-
await
|
|
211
|
-
pre = await preflight();
|
|
212
|
-
}
|
|
213
|
-
independentApps(pre.values);
|
|
214
|
-
if (api.served && api.generatesKey) {
|
|
215
|
-
// Past every refusal the pre-flight makes, and read again, so this deploy carries it.
|
|
216
|
-
const unprotected = await writeApiKey(agentDir).catch(failStartup);
|
|
217
|
-
if (unprotected !== undefined)
|
|
218
|
-
console.error(`[fastagent] note: ${unprotectedSecretsNote(unprotected)}`);
|
|
188
|
+
await prepareChannelApps(agentDir, unprepared, again, answerer.kind === "ticket" ? answerer.step.person : undefined).catch(failStartup);
|
|
219
189
|
pre = await preflight();
|
|
220
190
|
}
|
|
221
191
|
for (const m of pre.messages)
|
|
222
192
|
console.error(`[fastagent] ${m.level}: ${m.text}`);
|
|
193
|
+
// The box starts the build, never the source: dist/ is made here, from what the image will hold.
|
|
194
|
+
const built = await buildProject(agentDir).catch(failStartup);
|
|
195
|
+
for (const warning of built.warnings)
|
|
196
|
+
console.error(`[fastagent] warn: ${warning}`);
|
|
223
197
|
const { channels } = pre;
|
|
224
198
|
warnHostOnlyFlags(host, opts);
|
|
225
199
|
// What follows is the host's work: a deadline meant for the person must never cut it halfway. Its own steps for
|
|
226
200
|
// the person (a host sign-in, the box's login) wait again, and are bounded again.
|
|
227
201
|
if (answerer.kind === "ticket")
|
|
228
202
|
answerer.step.person.carriesOn();
|
|
203
|
+
let textDelivered = false;
|
|
229
204
|
const reachedAt = await target.deploy({
|
|
230
205
|
opts,
|
|
231
206
|
answerer,
|
|
@@ -233,6 +208,7 @@ async function deploy(host, dirArg, opts, step) {
|
|
|
233
208
|
agentDir,
|
|
234
209
|
config,
|
|
235
210
|
pre,
|
|
211
|
+
api,
|
|
236
212
|
channels,
|
|
237
213
|
webhookChannels: channels.filter((channel) => channel.ingress === "webhook"),
|
|
238
214
|
// The ownership predicate is bound HERE, from the same lookup that chose the host, so no host module can pass
|
|
@@ -249,10 +225,20 @@ async function deploy(host, dirArg, opts, step) {
|
|
|
249
225
|
`would deploy from them. Re-run with --force to regenerate, or remove each file's generated-by ` +
|
|
250
226
|
`marker to own it yourself.`));
|
|
251
227
|
}
|
|
228
|
+
// Once, after the gate and before the first artifact lands: the directory is replaced whole, so a refused
|
|
229
|
+
// run must leave it as it was, and the build outputs the host writes next go into the fresh one.
|
|
230
|
+
if (!textDelivered) {
|
|
231
|
+
textDelivered = true;
|
|
232
|
+
const copied = await deliverText(agentDir, delivered?.source).catch(failStartup);
|
|
233
|
+
if (copied > 0) {
|
|
234
|
+
console.error(`[fastagent] delivered ${copied} path(s) of the instance's text into ${DELIVERED_CONFIG_DIR}/`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
252
237
|
await applyArtifactPlan(plan);
|
|
253
238
|
},
|
|
254
239
|
});
|
|
255
|
-
const
|
|
240
|
+
const keyInFile = apiKeysFrom(loadEnvValues(dotEnvPath(agentDir)).get(API_KEY_VARIABLE)).length > 0;
|
|
241
|
+
const notes = apiReport(api, { valueFile: pre.valueFile, agentArg, keyInFile, run: !!opts.run }, reachedAt);
|
|
256
242
|
for (const line of notes)
|
|
257
243
|
console.error(`[fastagent] ${line}`);
|
|
258
244
|
return { ...(reachedAt === undefined ? {} : { url: reachedAt }), notes };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type InstanceFlags } from "../instance.ts";
|
|
1
2
|
export interface DevOptions {
|
|
2
3
|
port?: string;
|
|
3
4
|
bind?: string;
|
|
@@ -7,5 +8,6 @@ export interface DevOptions {
|
|
|
7
8
|
tunnel?: boolean;
|
|
8
9
|
/** false ⇔ `--no-input`. */
|
|
9
10
|
input?: boolean;
|
|
11
|
+
instance?: InstanceFlags;
|
|
10
12
|
}
|
|
11
13
|
export declare function runDev(dirArg: string, opts: DevOptions): Promise<void>;
|
package/dist/cli/commands/dev.js
CHANGED
|
@@ -6,10 +6,13 @@ import { listenForRestart, runDevSupervisor } from "../../dev-supervisor.js";
|
|
|
6
6
|
import { setLogLevel } from "../../log.js";
|
|
7
7
|
import { markDevServe } from "../../serving-command.js";
|
|
8
8
|
import { selectAgentEnvironment } from "../../paths.js";
|
|
9
|
+
import { loadDevProject } from "../../harnesses/pi-durable/open.js";
|
|
9
10
|
import { openServing } from "../../harnesses/pi-durable/serve.js";
|
|
10
11
|
import { DEFAULT_HTTP_PORT, serveAgent } from "../../service.js";
|
|
11
12
|
import { failStartup } from "../fail.js";
|
|
12
13
|
import { assertTunnelBindable, cliMountOptions, serveService } from "../serve.js";
|
|
14
|
+
import { setUpMissingChannels } from "../first-run-channels.js";
|
|
15
|
+
import { instanceInputs } from "../instance.js";
|
|
13
16
|
import { enterAgentCommand, parseBind, parsePort, reportAssembly } from "../shared.js";
|
|
14
17
|
/**
|
|
15
18
|
* `dev` binds loopback unless `--bind` says otherwise: a dev serve carries the agent's full tool authority and lands on
|
|
@@ -26,6 +29,9 @@ export async function runDev(dirArg, opts) {
|
|
|
26
29
|
process.on("message", listenForRestart(() => process.kill(process.pid, "SIGTERM")));
|
|
27
30
|
// The model is picked ONCE, in the parent process (a TTY; watch and --no-watch both).
|
|
28
31
|
const { agentDir, modelSpec } = await enterAgentCommand(dirArg, { ...opts, input: isWorker ? false : opts.input });
|
|
32
|
+
// Once, in the parent: the worker inherits what it writes, and a restart does not ask again.
|
|
33
|
+
if (!isWorker)
|
|
34
|
+
await setUpMissingChannels(agentDir, opts);
|
|
29
35
|
if (isWorker || opts.watch === false) {
|
|
30
36
|
await serveOnce(agentDir, modelSpec, opts);
|
|
31
37
|
return;
|
|
@@ -41,9 +47,15 @@ async function serveOnce(agentDir, modelSpec, opts) {
|
|
|
41
47
|
const host = parseBind(opts.bind) ?? DEV_BIND;
|
|
42
48
|
const tunnel = opts.tunnel ?? false;
|
|
43
49
|
assertTunnelBindable(host, tunnel);
|
|
44
|
-
const opened = await
|
|
50
|
+
const opened = await loadDevProject(agentDir)
|
|
51
|
+
.then(({ definition, instance }) => openServing(definition, {
|
|
52
|
+
model: modelSpec,
|
|
53
|
+
...instanceInputs(agentDir),
|
|
54
|
+
...(instance === undefined ? {} : { config: instance }),
|
|
55
|
+
}))
|
|
56
|
+
.catch(failStartup);
|
|
45
57
|
await reportAssembly(opened, modelSpec);
|
|
46
|
-
const { config } = opened
|
|
58
|
+
const { config } = opened;
|
|
47
59
|
const service = await serveAgent(opened, cliMountOptions(config)).catch(failStartup);
|
|
48
|
-
serveService(service, { port: portFlag ?? config.http?.port ?? DEFAULT_HTTP_PORT, host }, { tunnel, agentDir,
|
|
60
|
+
serveService(service, { port: portFlag ?? config.http?.port ?? DEFAULT_HTTP_PORT, host }, { tunnel, agentDir, config });
|
|
49
61
|
}
|
package/dist/cli/commands/env.js
CHANGED
|
@@ -100,7 +100,7 @@ export async function runEnvList(opts = {}) {
|
|
|
100
100
|
const missing = missingAptPackages(declared.aptPackages);
|
|
101
101
|
if (missing && missing.length > 0)
|
|
102
102
|
log.warn(`[fastagent] ${aptAdvice(missing)}`);
|
|
103
|
-
const unset = missingSecrets(variables).map(({ name }) => name);
|
|
103
|
+
const unset = missingSecrets(variables, process.env).map(({ name }) => name);
|
|
104
104
|
if (unset.length > 0) {
|
|
105
105
|
log.warn(`[fastagent] no value here for ${unset.join(", ")}: dev and start refuse to run until they have one — set ` +
|
|
106
106
|
`them in ${dotEnvPath(agentDir)}`);
|
|
@@ -1,48 +1,67 @@
|
|
|
1
1
|
/** `fastagent info [agent] [--json]`: print what the directory builds into, WITHOUT opening or serving the agent. */
|
|
2
|
+
import { definedValues } from "../../declared-secrets.js";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
import { channelIngress } from "../../channels/discover.js";
|
|
4
5
|
import { loadContent, loadEnvironment } from "../../content/file.js";
|
|
5
|
-
import {
|
|
6
|
+
import { resolveContent } from "../../content/resolve.js";
|
|
6
7
|
import { describeSecrets, missingSecrets } from "../../declared-secrets.js";
|
|
7
|
-
import {
|
|
8
|
+
import { enteredInstanceName, instanceName } from "../../instance-name.js";
|
|
9
|
+
import { enterInstance } from "../instance.js";
|
|
8
10
|
import { modelEnvironment } from "../../harnesses/models/environment.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
11
|
+
import { configValue, resolveConfig } from "../../harnesses/pi-durable/config.js";
|
|
12
|
+
import { definitionSecrets } from "../../harnesses/pi-durable/definition.js";
|
|
13
|
+
import { buildDefinition, loadDevProject, modelSpecFor } from "../../harnesses/pi-durable/open.js";
|
|
14
|
+
import { openText, readCommands } from "../../harnesses/pi-durable/text.js";
|
|
15
|
+
import { inspectMcp } from "../../mcp/host.js";
|
|
16
|
+
import { resolveStateRoot, resolveWorkdir } from "../../paths.js";
|
|
12
17
|
import { nextRun } from "../../schedule/cron.js";
|
|
13
18
|
import { contentLines } from "../content-view.js";
|
|
14
19
|
import { agentDirOrExit, failStartup } from "../fail.js";
|
|
15
20
|
export async function runInfo(dirArg, opts) {
|
|
16
21
|
const agentDir = agentDirOrExit(resolve(dirArg));
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const {
|
|
22
|
+
enterInstance(agentDir, opts.instance); // code may read its secrets, and fetch, when it is built
|
|
23
|
+
const workdir = resolveWorkdir(agentDir);
|
|
24
|
+
const { definition, instance } = await loadDevProject(agentDir).catch(failStartup);
|
|
25
|
+
const config = (() => {
|
|
26
|
+
try {
|
|
27
|
+
return resolveConfig(definition, instance);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
return failStartup(error);
|
|
31
|
+
}
|
|
32
|
+
})();
|
|
33
|
+
const name = instanceName(definition.name, enteredInstanceName() ?? config.name);
|
|
20
34
|
const modelSpec = modelSpecFor(config, opts.model);
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
const declared = (() => {
|
|
36
|
+
try {
|
|
37
|
+
return definitionSecrets(definition, config, instance);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return failStartup(error);
|
|
41
|
+
}
|
|
42
|
+
})();
|
|
43
|
+
const unsetSecrets = missingSecrets(declared, process.env);
|
|
24
44
|
// Built only when every required secret has a value: building is what reads them. A module that is not what its
|
|
25
45
|
// directory holds is said as the refusal `dev` and `start` would stop on.
|
|
26
46
|
const code = unsetSecrets.length === 0
|
|
27
47
|
? await Promise.resolve()
|
|
28
|
-
.then(() => buildDefinition(definition))
|
|
48
|
+
.then(() => buildDefinition(definition, process.env, config))
|
|
29
49
|
.catch(failStartup)
|
|
30
50
|
: undefined;
|
|
31
51
|
// Reported, not fatal: `info` is how an author finds out a content entry is missing, or the environment refused.
|
|
32
52
|
let content = [];
|
|
33
|
-
let contentAbsent = [];
|
|
34
53
|
let contentError;
|
|
35
54
|
try {
|
|
36
|
-
|
|
37
|
-
content = resolveContent(agentDir, declaration);
|
|
38
|
-
contentAbsent = contentAbsentHere(declaration);
|
|
55
|
+
content = resolveContent(loadContent(agentDir, instance), { dir: agentDir, workdir });
|
|
39
56
|
}
|
|
40
57
|
catch (error) {
|
|
41
58
|
contentError = error.message;
|
|
42
59
|
}
|
|
43
60
|
// Read already, by the definition's secrets: a refused environment is a definition that does not open.
|
|
44
61
|
const environment = loadEnvironment(agentDir);
|
|
45
|
-
const models = await modelEnvironment(agentDir, {}, code?.providers ?? []
|
|
62
|
+
const models = await modelEnvironment(agentDir, {}, code?.providers ?? [], {
|
|
63
|
+
environment: definedValues(process.env),
|
|
64
|
+
});
|
|
46
65
|
const slash = modelSpec?.indexOf("/") ?? -1;
|
|
47
66
|
const modelError = modelSpec === undefined || models.models.getModel(modelSpec.slice(0, slash), modelSpec.slice(slash + 1))
|
|
48
67
|
? undefined
|
|
@@ -57,27 +76,33 @@ export async function runInfo(dirArg, opts) {
|
|
|
57
76
|
tz: s.tz ?? null,
|
|
58
77
|
next: nextRun(s.cron, s.tz, new Date())?.toISOString() ?? null,
|
|
59
78
|
}));
|
|
60
|
-
const
|
|
79
|
+
const text = await Promise.resolve()
|
|
80
|
+
.then(() => openText(agentDir, instance))
|
|
81
|
+
.catch(failStartup);
|
|
82
|
+
const commands = readCommands(text).map(({ name, description }) => ({ name, description: description ?? null }));
|
|
61
83
|
const stateRoot = resolveStateRoot(agentDir);
|
|
62
84
|
const auth = models.auth;
|
|
85
|
+
const mcp = await inspectMcp(agentDir, instance === undefined ? {} : { config: instance.label }).catch(failStartup);
|
|
63
86
|
if (opts.json) {
|
|
64
87
|
console.log(JSON.stringify({
|
|
65
88
|
agentDir,
|
|
66
|
-
name:
|
|
89
|
+
name: name,
|
|
67
90
|
content,
|
|
68
|
-
contentAbsent,
|
|
69
91
|
contentError: contentError ?? null,
|
|
70
92
|
environment: environment ?? null,
|
|
71
93
|
model: modelSpec ?? null,
|
|
72
94
|
modelError: modelError ?? null,
|
|
73
95
|
thinkingLevel: config.thinkingLevel ?? null,
|
|
96
|
+
config: configReport(config, opts.model),
|
|
74
97
|
tools: code?.tools.map(({ name }) => name) ?? null,
|
|
98
|
+
mcp,
|
|
75
99
|
extensions: code?.extensions.map(({ name }) => name) ?? null,
|
|
76
100
|
channels: code === undefined ? null : channels,
|
|
77
101
|
schedules: code === undefined ? null : schedules,
|
|
78
102
|
commands,
|
|
79
|
-
secrets:
|
|
103
|
+
secrets: definition.inputs.secrets,
|
|
80
104
|
unsetSecrets: unsetSecrets.map(({ name }) => name),
|
|
105
|
+
workdir,
|
|
81
106
|
stateRoot,
|
|
82
107
|
authPath: auth?.path ?? null,
|
|
83
108
|
fallbackAuthPath: auth?.fallback ?? null,
|
|
@@ -87,13 +112,13 @@ export async function runInfo(dirArg, opts) {
|
|
|
87
112
|
const line = (label, value) => console.log(`${`${label}:`.padEnd(13)} ${value}`);
|
|
88
113
|
/** A continuation under the previous line, aligned to the same column (no label, so no bare colon). */
|
|
89
114
|
const cont = (value) => console.log(`${"".padEnd(13)} ${value}`);
|
|
90
|
-
line("agent", `${
|
|
91
|
-
for (const [label, value] of contentLines(content
|
|
115
|
+
line("agent", `${name} — ${agentDir}`);
|
|
116
|
+
for (const [label, value] of contentLines(content))
|
|
92
117
|
line(label, value);
|
|
93
118
|
if (contentError)
|
|
94
119
|
cont(`⚠ ${contentError}`);
|
|
95
120
|
line("environment", environment ? describeEnvironment(environment) : "(none: the machine's commands)");
|
|
96
|
-
line("model", modelSpec ?? "(not set — pass --model, set FASTAGENT_MODEL, or model in fastagent.config.ts)");
|
|
121
|
+
line("model", modelSpec ?? "(not set — pass --model, set FASTAGENT_MODEL, or defaults.model in fastagent.config.ts)");
|
|
97
122
|
if (modelError)
|
|
98
123
|
cont(`⚠ does not resolve: ${modelError}`);
|
|
99
124
|
if (config.thinkingLevel)
|
|
@@ -102,6 +127,7 @@ export async function runInfo(dirArg, opts) {
|
|
|
102
127
|
line("tools", code ? code.tools.map(({ name }) => name).join(", ") || "(none)" : unbuilt);
|
|
103
128
|
if (code && code.extensions.length > 0)
|
|
104
129
|
line("extensions", code.extensions.map(({ name }) => name).join(", "));
|
|
130
|
+
line("mcp", mcp.map((server) => `${server.name} (${server.transport}, ${server.authorization})`).join(", ") || "(none)");
|
|
105
131
|
line("channels", code ? channels.map((c) => `${c.name} (${c.ingress})`).join(", ") || "(none)" : unbuilt);
|
|
106
132
|
line("schedules", code ? schedules.map((s) => `${s.name} (next ${s.next ?? "never"})`).join(", ") || "(none)" : unbuilt);
|
|
107
133
|
line("commands", commands.map(({ name }) => `/${name}`).join(", ") || "(none)");
|
|
@@ -109,10 +135,23 @@ export async function runInfo(dirArg, opts) {
|
|
|
109
135
|
if (unsetSecrets.length > 0) {
|
|
110
136
|
cont(`⚠ dev, start and deployments refuse to boot until set: ${unsetSecrets.map((s) => s.name).join(", ")}`);
|
|
111
137
|
}
|
|
138
|
+
line("workdir", workdir);
|
|
112
139
|
line("state", stateRoot);
|
|
113
140
|
if (auth)
|
|
114
141
|
line("auth", auth.fallback === undefined ? auth.path : `${auth.path} (then ${auth.fallback})`);
|
|
115
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Each value the configuration resolved, by its path, with where it is from: the program's defaults, the instance's
|
|
145
|
+
* file, and for the model the flag or variable that overrides both.
|
|
146
|
+
*/
|
|
147
|
+
function configReport(config, modelFlag) {
|
|
148
|
+
const report = Object.fromEntries(Object.entries(config.sources).map(([path, source]) => [path, { value: configValue(config, path), source }]));
|
|
149
|
+
const override = modelFlag !== undefined ? "--model" : process.env.FASTAGENT_MODEL ? "FASTAGENT_MODEL" : undefined;
|
|
150
|
+
const model = modelSpecFor(config, modelFlag);
|
|
151
|
+
if (override !== undefined && model !== undefined)
|
|
152
|
+
report.model = { value: model, source: override };
|
|
153
|
+
return report;
|
|
154
|
+
}
|
|
116
155
|
/** The packages and apt packages an environment declares, as one line. */
|
|
117
156
|
function describeEnvironment(environment) {
|
|
118
157
|
const packages = Object.entries(environment.packages).map(([name, version]) => `${name} ${version}`);
|
|
@@ -15,7 +15,9 @@ export async function runInit(dirArg, opts) {
|
|
|
15
15
|
.catch(failStartup);
|
|
16
16
|
let installFailed = false;
|
|
17
17
|
const { created, content, repository } = await initAgent(dir, {
|
|
18
|
-
|
|
18
|
+
// A new agent's repositories are its program's: every instance of it reaches them, and a deploy carries them. A
|
|
19
|
+
// directory is this machine's, and goes to the instance configuration.
|
|
20
|
+
content: read.map(({ addition }) => ("github" in addition.entry ? { ...addition, program: true } : addition)),
|
|
19
21
|
...(opts.install
|
|
20
22
|
? {
|
|
21
23
|
install: async (agentDir) => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type InstanceFlags } from "../instance.ts";
|
|
1
2
|
export interface InvokeOptions {
|
|
2
3
|
model?: string;
|
|
3
4
|
/** false ⇔ `--no-input`. */
|
|
@@ -8,5 +9,6 @@ export interface InvokeOptions {
|
|
|
8
9
|
url?: string;
|
|
9
10
|
/** The key `url` is called with; else the first in the agent's production values. */
|
|
10
11
|
key?: string;
|
|
12
|
+
instance?: InstanceFlags;
|
|
11
13
|
}
|
|
12
14
|
export declare function runInvoke(message: string, dirArg: string, opts: InvokeOptions): Promise<void>;
|
|
@@ -14,6 +14,7 @@ import { API_KEY_VARIABLE, apiKeysFrom } from "../../http/server.js";
|
|
|
14
14
|
import { resolveStateRoot, selectAgentEnvironment } from "../../paths.js";
|
|
15
15
|
import { runInvokeJson, runInvokeStream } from "../invoke-stream.js";
|
|
16
16
|
import { failStartup, failUsage, optionalAgentDirOrExit } from "../fail.js";
|
|
17
|
+
import { enterEnvFile, instanceInputs } from "../instance.js";
|
|
17
18
|
/**
|
|
18
19
|
* `invoke`'s sessions, apart from the instance's storage, so `invoke` runs beside a serve of this directory, which holds
|
|
19
20
|
* that one; one `invoke` at a time holds this one.
|
|
@@ -37,11 +38,15 @@ async function sessionFor(agent, sessionId, settings, file) {
|
|
|
37
38
|
/** A refusal as the line an operator reads: its code, then what it says. */
|
|
38
39
|
const refusal = (error) => error instanceof AgentError ? new Error(`${error.code}: ${error.message}`) : error;
|
|
39
40
|
/** Print the message's answer as it is written, then exit with what it ended as: 0 only for `completed`. */
|
|
40
|
-
|
|
41
|
+
/** What to do about a model with no credential, where the answer came from. */
|
|
42
|
+
const LOCAL_AUTH_HINT = "sign in with `fastagent login`, or set the provider's API key in .secrets/.env";
|
|
43
|
+
const DEPLOYED_AUTH_HINT = "give the deployment a credential: `fastagent secrets set <host> <the provider's API key variable>` " +
|
|
44
|
+
"(`fastagent secrets list <host>` names it), or sign its box in with `fastagent login <provider> --deployment <host>`";
|
|
45
|
+
async function answer(events, json, authHint) {
|
|
41
46
|
// Both renderers exit 1 unless the message completed: CI gates on it.
|
|
42
47
|
const exitCode = json
|
|
43
48
|
? await runInvokeJson(events, (line) => process.stdout.write(`${line}\n`))
|
|
44
|
-
: await runInvokeStream(events, (text) => process.stdout.write(text), (line) => console.error(line));
|
|
49
|
+
: await runInvokeStream(events, (text) => process.stdout.write(text), (line) => console.error(line), authHint);
|
|
45
50
|
if (!json)
|
|
46
51
|
process.stdout.write("\n");
|
|
47
52
|
return exitCode;
|
|
@@ -79,6 +84,10 @@ function enterForUrl(dirArg, key, url) {
|
|
|
79
84
|
/** `--url`: the message sent to a served agent over its Agent API, in a session there; nothing opens here. */
|
|
80
85
|
async function runRemoteInvoke(message, dirArg, opts) {
|
|
81
86
|
const { url } = opts;
|
|
87
|
+
const { envFile, ...placed } = opts.instance ?? {};
|
|
88
|
+
for (const flag of Object.keys(placed))
|
|
89
|
+
failUsage(`--${flag} places an instance this run opens, and --url opens none`);
|
|
90
|
+
enterEnvFile(envFile);
|
|
82
91
|
let settings;
|
|
83
92
|
try {
|
|
84
93
|
settings = opts.model === undefined ? undefined : { model: modelRefOf(opts.model, "--model") };
|
|
@@ -90,7 +99,7 @@ async function runRemoteInvoke(message, dirArg, opts) {
|
|
|
90
99
|
console.error(`[fastagent] invoke: ${url}`);
|
|
91
100
|
const sessionId = await remoteSession(agent, opts.session, settings, url).catch((error) => failStartup(refusal(error)));
|
|
92
101
|
console.error(`[fastagent] session ${sessionId} (continue it with --session ${sessionId})`);
|
|
93
|
-
process.exit(await answer(agent.messages.stream({ sessionId, content: message }), opts.json));
|
|
102
|
+
process.exit(await answer(agent.messages.stream({ sessionId, content: message }), opts.json, DEPLOYED_AUTH_HINT));
|
|
94
103
|
}
|
|
95
104
|
/** The served agent's session to send into: a new one, or `sessionId`, set to `--model` when one is given. */
|
|
96
105
|
async function remoteSession(agent, sessionId, settings, url) {
|
|
@@ -110,28 +119,32 @@ export async function runInvoke(message, dirArg, opts) {
|
|
|
110
119
|
return runRemoteInvoke(message, dirArg, { ...opts, url: opts.url });
|
|
111
120
|
if (opts.key !== undefined)
|
|
112
121
|
failUsage("--key is the key --url calls with: pass --url too");
|
|
113
|
-
const [{ openServing }, { enterAgentCommand, reportAuth }] = await Promise.all([
|
|
122
|
+
const [{ openServing }, { loadDevProject }, { enterAgentCommand, reportAuth }] = await Promise.all([
|
|
114
123
|
import("../../harnesses/pi-durable/serve.js"),
|
|
124
|
+
import("../../harnesses/pi-durable/open.js"),
|
|
115
125
|
import("../shared.js"),
|
|
116
126
|
]);
|
|
117
127
|
const { agentDir, modelSpec } = await enterAgentCommand(dirArg, opts);
|
|
118
128
|
const file = invokeStorageFile(resolveStateRoot(agentDir));
|
|
119
|
-
const
|
|
129
|
+
const project = await loadDevProject(agentDir).catch(failStartup);
|
|
130
|
+
const { agent, models, config } = await openServing(project.definition, {
|
|
120
131
|
model: modelSpec,
|
|
132
|
+
...instanceInputs(agentDir),
|
|
133
|
+
...(project.instance === undefined ? {} : { config: project.instance }),
|
|
121
134
|
storage: { file, holders: "another `fastagent invoke` of this agent" },
|
|
122
135
|
// Its messages do not outlive it: one a stopped invoke left is not run again by the next.
|
|
123
136
|
unfinished: "abort",
|
|
124
137
|
}).catch(failStartup);
|
|
125
138
|
console.error(`[fastagent] invoke: ${agentDir} (${modelSpec})`);
|
|
126
139
|
await reportAuth(models, modelSpec, agentDir);
|
|
127
|
-
const { thinkingLevel } =
|
|
140
|
+
const { thinkingLevel } = config;
|
|
128
141
|
const settings = {
|
|
129
142
|
model: modelRefOf(modelSpec, "the model"),
|
|
130
143
|
...(thinkingLevel === undefined ? {} : { thinkingLevel }),
|
|
131
144
|
};
|
|
132
145
|
const sessionId = await sessionFor(agent, opts.session, settings, file).catch(failStartup);
|
|
133
146
|
console.error(`[fastagent] session ${sessionId} (continue it with --session ${sessionId})`);
|
|
134
|
-
const exitCode = await answer(agent.messages.stream({ sessionId, content: message }), opts.json);
|
|
147
|
+
const exitCode = await answer(agent.messages.stream({ sessionId, content: message }), opts.json, LOCAL_AUTH_HINT);
|
|
135
148
|
await agent.close();
|
|
136
149
|
// Always exit explicitly: the undici proxy agent's keep-alive sockets would otherwise hold the event loop open
|
|
137
150
|
// after a successful one-shot message.
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `fastagent login [provider]`: sign in to a model provider, into the project-level auth file
|
|
3
|
-
* (`<agentDir>/.secrets/auth.json`) by default, or `FASTAGENT_AUTH_PATH`. In a terminal it asks, as pi's `/login`
|
|
4
|
-
* does; otherwise (a script, a coding agent: no terminal, `--no-input` or `--json`) nothing is asked and nothing
|
|
5
|
-
* waits: what it needs is named, and a person's step is handed back with a ticket to check (ticket.ts).
|
|
6
|
-
* `--deployment` runs the same login on this agent's deployed box instead (box-login.ts); `--stdio` is the box's half.
|
|
7
|
-
*/
|
|
8
1
|
import type { AuthInteraction } from "@earendil-works/pi-ai";
|
|
9
2
|
import { type TicketStep } from "../ticket.ts";
|
|
10
3
|
export interface LoginOptions {
|