@fastagent-sh/fastagent 0.19.0 → 0.21.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 +5 -1
- package/dist/atomic-write.d.ts +11 -7
- package/dist/atomic-write.js +16 -11
- package/dist/channels/agentcore-limits.d.ts +9 -0
- package/dist/channels/agentcore-limits.js +9 -0
- package/dist/channels/agentcore-protocol.d.ts +112 -0
- package/dist/channels/agentcore-protocol.js +22 -0
- package/dist/channels/agentcore-service.d.ts +12 -15
- package/dist/channels/agentcore-service.js +15 -24
- package/dist/channels/agentcore-state.d.ts +5 -11
- package/dist/channels/agentcore-state.js +4 -1
- package/dist/channels/agentcore.d.ts +9 -83
- package/dist/channels/agentcore.js +101 -93
- package/dist/channels/control.d.ts +50 -12
- package/dist/channels/control.js +251 -157
- package/dist/channels/discover.d.ts +26 -5
- package/dist/channels/discover.js +13 -26
- package/dist/channels/feishu/context-buffer.d.ts +6 -0
- package/dist/channels/feishu/context-buffer.js +0 -38
- package/dist/channels/feishu/crypto.d.ts +0 -2
- package/dist/channels/feishu/crypto.js +3 -8
- package/dist/channels/feishu/feishu-api.js +7 -12
- package/dist/channels/feishu/feishu.js +86 -128
- package/dist/channels/feishu/parse.d.ts +4 -3
- package/dist/channels/feishu/parse.js +5 -4
- package/dist/channels/feishu/register-webhook.d.ts +5 -5
- package/dist/channels/feishu/register-webhook.js +43 -57
- package/dist/channels/feishu/scaffold/feishu-send.ts +12 -67
- package/dist/channels/feishu/setup-mode.d.ts +30 -0
- package/dist/channels/feishu/setup-mode.js +26 -0
- package/dist/channels/feishu/shared-api.d.ts +10 -0
- package/dist/channels/feishu/shared-api.js +38 -0
- package/dist/channels/http.d.ts +0 -8
- package/dist/channels/http.js +4 -56
- package/dist/channels/kit/attachment-path.d.ts +12 -0
- package/dist/channels/kit/attachment-path.js +43 -0
- package/dist/channels/kit/context-buffer.d.ts +9 -0
- package/dist/channels/kit/context-buffer.js +11 -0
- package/dist/channels/kit/signature.d.ts +12 -0
- package/dist/channels/kit/signature.js +17 -0
- package/dist/channels/kit/stop-command.js +3 -3
- package/dist/channels/kit/tasks.d.ts +1 -1
- package/dist/channels/kit/tasks.js +10 -4
- package/dist/channels/kit/turn-runner.d.ts +59 -0
- package/dist/channels/kit/turn-runner.js +84 -0
- package/dist/channels/kit/turn-store.d.ts +19 -3
- package/dist/channels/kit/turn-store.js +29 -2
- package/dist/channels/lark/scaffold/lark-send.ts +12 -67
- package/dist/channels/registration.d.ts +36 -1
- package/dist/channels/registration.js +57 -1
- package/dist/channels/secret.d.ts +1 -0
- package/dist/channels/secret.js +16 -0
- package/dist/channels/slack/config-api.d.ts +35 -3
- package/dist/channels/slack/config-api.js +51 -9
- package/dist/channels/slack/invoke-turn.d.ts +1 -1
- package/dist/channels/slack/invoke-turn.js +5 -1
- package/dist/channels/slack/manifest.js +5 -1
- package/dist/channels/slack/onboard.d.ts +5 -5
- package/dist/channels/slack/onboard.js +44 -23
- package/dist/channels/slack/onboarding-state.d.ts +7 -3
- package/dist/channels/slack/onboarding-state.js +13 -20
- package/dist/channels/slack/register-webhook.d.ts +3 -2
- package/dist/channels/slack/register-webhook.js +35 -15
- package/dist/channels/slack/scaffold/channel.ts +3 -10
- package/dist/channels/slack/scaffold/slack-send.ts +18 -141
- package/dist/channels/slack/shared-api.d.ts +10 -0
- package/dist/channels/slack/shared-api.js +34 -0
- package/dist/channels/slack/slack-api.d.ts +20 -2
- package/dist/channels/slack/slack-api.js +100 -73
- package/dist/channels/slack/slack.d.ts +0 -10
- package/dist/channels/slack/slack.js +68 -107
- package/dist/channels/sse.d.ts +4 -0
- package/dist/channels/sse.js +66 -0
- package/dist/channels/telegram/parse.d.ts +21 -1
- package/dist/channels/telegram/parse.js +65 -11
- package/dist/channels/telegram/register-webhook.d.ts +6 -9
- package/dist/channels/telegram/register-webhook.js +44 -42
- package/dist/channels/telegram/scaffold/channel.ts +7 -3
- package/dist/channels/telegram/telegram-api.js +5 -6
- package/dist/channels/telegram/telegram.d.ts +2 -2
- package/dist/channels/telegram/telegram.js +97 -204
- package/dist/channels/wait-health.js +7 -4
- package/dist/cli/add-feishu.js +3 -10
- package/dist/cli/add-slack.js +13 -25
- package/dist/cli/commands/add.d.ts +0 -1
- package/dist/cli/commands/add.js +22 -35
- package/dist/cli/commands/attach.d.ts +2 -4
- package/dist/cli/commands/attach.js +17 -15
- package/dist/cli/commands/chat.js +6 -12
- package/dist/cli/commands/deploy/agentcore.d.ts +2 -0
- package/dist/cli/commands/deploy/agentcore.js +178 -0
- package/dist/cli/commands/deploy/docker.d.ts +2 -0
- package/dist/cli/commands/deploy/docker.js +119 -0
- package/dist/cli/commands/deploy/fly.d.ts +2 -0
- package/dist/cli/commands/deploy/fly.js +131 -0
- package/dist/cli/commands/deploy/railway.d.ts +2 -0
- package/dist/cli/commands/deploy/railway.js +71 -0
- package/dist/cli/commands/deploy/shared.d.ts +114 -0
- package/dist/cli/commands/deploy/shared.js +124 -0
- package/dist/cli/commands/deploy.d.ts +38 -25
- package/dist/cli/commands/deploy.js +101 -631
- package/dist/cli/commands/dev.js +20 -76
- package/dist/cli/commands/fire.js +6 -12
- package/dist/cli/commands/info.js +1 -1
- package/dist/cli/commands/init.js +1 -2
- package/dist/cli/commands/invoke.js +4 -11
- package/dist/cli/commands/schedule.js +1 -1
- package/dist/cli/commands/start.js +41 -80
- package/dist/cli/commands/tool.js +3 -8
- package/dist/cli/kernel.d.ts +0 -2
- package/dist/cli/kernel.js +0 -2
- package/dist/cli/program.js +7 -12
- package/dist/cli/serve.d.ts +42 -34
- package/dist/cli/serve.js +107 -28
- package/dist/cli/shared.d.ts +52 -27
- package/dist/cli/shared.js +69 -6
- package/dist/deploy/agentcore/forwarder.js +250 -0
- package/dist/deploy/agentcore/logs.d.ts +2 -2
- package/dist/deploy/agentcore/logs.js +2 -2
- package/dist/deploy/agentcore/plan.d.ts +34 -11
- package/dist/deploy/agentcore/plan.js +52 -259
- package/dist/deploy/agentcore/run.d.ts +9 -24
- package/dist/deploy/agentcore/run.js +35 -43
- package/dist/deploy/channel-ingress.d.ts +73 -0
- package/dist/deploy/channel-ingress.js +101 -0
- package/dist/deploy/docker/plan.d.ts +3 -7
- package/dist/deploy/docker/plan.js +4 -15
- package/dist/deploy/docker/run.d.ts +35 -4
- package/dist/deploy/docker/run.js +40 -8
- package/dist/deploy/fly/plan.d.ts +4 -5
- package/dist/deploy/fly/plan.js +14 -22
- package/dist/deploy/fly/run.d.ts +36 -23
- package/dist/deploy/fly/run.js +129 -83
- package/dist/deploy/hosts.d.ts +5 -0
- package/dist/deploy/hosts.js +4 -0
- package/dist/deploy/preflight.d.ts +6 -7
- package/dist/deploy/preflight.js +27 -23
- package/dist/deploy/railway/plan.d.ts +9 -5
- package/dist/deploy/railway/plan.js +18 -26
- package/dist/deploy/railway/run.d.ts +11 -10
- package/dist/deploy/railway/run.js +44 -43
- package/dist/deploy/runner.js +22 -2
- package/dist/deploy/secrets.d.ts +18 -9
- package/dist/deploy/secrets.js +63 -28
- package/dist/dev-supervisor.js +3 -4
- package/dist/engines/pi/agent-session-factory.d.ts +37 -17
- package/dist/engines/pi/agent-session-factory.js +111 -106
- package/dist/engines/pi/auth.js +43 -43
- package/dist/engines/pi/config.d.ts +2 -2
- package/dist/engines/pi/create.d.ts +22 -27
- package/dist/engines/pi/create.js +59 -74
- package/dist/engines/pi/definition.d.ts +1 -1
- package/dist/engines/pi/definition.js +11 -12
- package/dist/engines/pi/invoke-session.js +13 -30
- package/dist/engines/pi/login.js +32 -16
- package/dist/engines/pi/open.d.ts +3 -4
- package/dist/engines/pi/open.js +32 -42
- package/dist/engines/pi/retry-event.d.ts +6 -0
- package/dist/engines/pi/retry-event.js +15 -0
- package/dist/engines/pi/search-tools.js +1 -1
- package/dist/engines/pi/service.d.ts +1 -1
- package/dist/engines/pi/service.js +8 -0
- package/dist/engines/pi/session-builder.js +26 -140
- package/dist/engines/pi/session-control.d.ts +27 -22
- package/dist/engines/pi/session-control.js +551 -490
- package/dist/engines/pi/session-inheritance.d.ts +8 -22
- package/dist/engines/pi/session-inheritance.js +95 -76
- package/dist/engines/pi/session-markers.d.ts +48 -0
- package/dist/engines/pi/session-markers.js +59 -0
- package/dist/engines/pi/session-settings.d.ts +5 -5
- package/dist/engines/pi/session-settings.js +8 -5
- package/dist/engines/pi/session-store.d.ts +91 -26
- package/dist/engines/pi/session-store.js +413 -82
- package/dist/engines/pi/tool-context.d.ts +24 -11
- package/dist/engines/pi/tool-context.js +29 -4
- package/dist/engines/pi/tool.d.ts +6 -9
- package/dist/engines/pi/tool.js +3 -2
- package/dist/env.js +1 -2
- package/dist/feishu.d.ts +1 -0
- package/dist/feishu.js +1 -0
- package/dist/lark.d.ts +1 -0
- package/dist/lark.js +1 -0
- package/dist/loader.d.ts +51 -7
- package/dist/loader.js +84 -18
- package/dist/log.d.ts +9 -17
- package/dist/log.js +25 -30
- package/dist/paths.d.ts +26 -3
- package/dist/paths.js +43 -5
- package/dist/scaffold/add-channel.d.ts +6 -1
- package/dist/scaffold/add-channel.js +50 -67
- package/dist/scaffold/init.js +7 -2
- package/dist/scaffold/templates/tools/fetch-url.ts +0 -2
- package/dist/schedule/discover.js +3 -15
- package/dist/schedule/wake-alarm.d.ts +14 -19
- package/dist/schedule/wake-alarm.js +89 -48
- package/dist/schedule/wakeups.d.ts +1 -1
- package/dist/schedule/wakeups.js +10 -7
- package/dist/service.d.ts +24 -24
- package/dist/service.js +33 -77
- package/dist/session-remote.d.ts +12 -7
- package/dist/session-remote.js +185 -118
- package/dist/session.d.ts +227 -93
- package/dist/session.js +61 -23
- package/dist/slack.d.ts +2 -0
- package/dist/slack.js +1 -0
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +1 -1
- package/dist/tunnel.d.ts +34 -9
- package/dist/tunnel.js +83 -50
- package/package.json +5 -4
- package/dist/channels/slack/bot-auth.d.ts +0 -15
- package/dist/channels/slack/bot-auth.js +0 -135
package/dist/cli/commands/dev.js
CHANGED
|
@@ -3,38 +3,23 @@
|
|
|
3
3
|
* assemble + serve, restarting it on agent edits. A fresh process per reload means what is served
|
|
4
4
|
* is always the latest code, including modules a tool/config imports.
|
|
5
5
|
*/
|
|
6
|
-
import { resolve } from "node:path";
|
|
7
6
|
import { runDevSupervisor } from "../../dev-supervisor.js";
|
|
8
|
-
import {
|
|
9
|
-
import { reportFindingsIfChanged, reportToolCollisions } from "../../engines/pi/report.js";
|
|
10
|
-
import { reportModuleLoadFailures } from "../../log.js";
|
|
11
|
-
import { CODING_TOOL_NAMES } from "../../engines/pi/create.js";
|
|
7
|
+
import { setLogLevel } from "../../log.js";
|
|
12
8
|
import { createPiAgentFromDir } from "../../engines/pi/open.js";
|
|
13
9
|
import { mountAgentService } from "../../service.js";
|
|
14
|
-
import { setLogLevel } from "../../log.js";
|
|
15
10
|
import { logAgentLoop } from "../../observe.js";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { failStartup, placementOrExit } from "../fail.js";
|
|
20
|
-
import { SHUTDOWN_GRACE_MS, assertTunnelBindable, maybeTunnel, reportServing, serve } from "../serve.js";
|
|
21
|
-
import { parseBind, parsePort, reportAuth, reportLine, resolveFirstRunModel, reportWorkspaceHint } from "../shared.js";
|
|
11
|
+
import { failStartup } from "../fail.js";
|
|
12
|
+
import { assertTunnelBindable, cliMountOptions, resolveBindHost, serveService } from "../serve.js";
|
|
13
|
+
import { enterAgentCommand, parseBind, parsePort, reportAssembly } from "../shared.js";
|
|
22
14
|
export async function runDev(dirArg, opts) {
|
|
23
|
-
const dir = resolve(dirArg);
|
|
24
|
-
const placement = placementOrExit(dir);
|
|
25
15
|
setLogLevel("debug"); // dev posture: verbose, includes the debug turn trace (content) — supervisor and worker both
|
|
26
16
|
const isWorker = process.env.FASTAGENT_DEV_WORKER === "1";
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
if (!isWorker) {
|
|
32
|
-
loadDotEnv(placement.agentDir);
|
|
33
|
-
installProxyFetch();
|
|
34
|
-
await resolveFirstRunModel(placement.agentDir, opts);
|
|
35
|
-
}
|
|
17
|
+
// The model is picked ONCE, in the parent process (a TTY; watch and --no-watch both); a spawned
|
|
18
|
+
// worker inherits the choice through FASTAGENT_MODEL and must not prompt even when the pick was
|
|
19
|
+
// cancelled.
|
|
20
|
+
const placement = await enterAgentCommand(dirArg, { ...opts, input: isWorker ? false : opts.input });
|
|
36
21
|
if (isWorker || opts.watch === false) {
|
|
37
|
-
await serveOnce(
|
|
22
|
+
await serveOnce(placement, opts);
|
|
38
23
|
return;
|
|
39
24
|
}
|
|
40
25
|
parsePort(opts.port, "--port", "flag"); // flag-shape checks before spawning
|
|
@@ -44,64 +29,23 @@ export async function runDev(dirArg, opts) {
|
|
|
44
29
|
await runDevSupervisor(placement, { tunnel: opts.tunnel ?? false });
|
|
45
30
|
}
|
|
46
31
|
/** Assemble the agent and serve it once (the dev worker; also the --no-watch path). */
|
|
47
|
-
async function serveOnce(
|
|
32
|
+
async function serveOnce(placement, opts) {
|
|
48
33
|
const portFlag = parsePort(opts.port, "--port", "flag");
|
|
49
34
|
const bindFlag = parseBind(opts.bind);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const a = await createPiAgentFromDir(dir, {
|
|
35
|
+
const tunnel = opts.tunnel ?? false;
|
|
36
|
+
const a = await createPiAgentFromDir(placement.workspace, {
|
|
53
37
|
model: opts.model,
|
|
54
38
|
authPath: opts.authPath, // flag > FASTAGENT_AUTH_PATH > default — resolved by the opener (one owner)
|
|
55
39
|
serving: true, // long-running serve: the scheduler poller runs (wake mounts iff config.selfSchedule)
|
|
56
40
|
}).catch(failStartup);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
reportLine("config", a.configPath ?? "(none)");
|
|
61
|
-
reportLine("model", `${a.modelSpec}${a.config.thinkingLevel ? ` (thinking: ${a.config.thinkingLevel})` : ""}`);
|
|
62
|
-
await reportAuth(a.agentDir, a.modelSpec, a.authPath);
|
|
63
|
-
reportAgentsSkillsTools(a);
|
|
64
|
-
// Trace each turn's agent loop (tool calls + reply) to the log at debug level — shown in dev, gated
|
|
65
|
-
// out in start (level info), keeping end-user content out of production logs. Wired in both postures.
|
|
66
|
-
// `http.host` enters here the way the flag enters `parseBind` — through `bindAddress`, so a
|
|
67
|
-
// configured `localhost` is an ADDRESS by the time anything binds, renders or dials it.
|
|
68
|
-
const configured = a.config.http?.host;
|
|
69
|
-
const host = bindFlag ?? (configured === undefined ? undefined : bindAddress(configured));
|
|
70
|
-
assertTunnelBindable(host, opts.tunnel ?? false, bindFlag ? "flag" : "config");
|
|
41
|
+
// The same report `start` prints; `config:` is dev's own extra (see reportAssembly on the asymmetry).
|
|
42
|
+
await reportAssembly(a, { beforeModel: [["config", a.configPath ?? "(none)"]] });
|
|
43
|
+
const host = resolveBindHost(bindFlag, a.config.http?.host, tunnel);
|
|
71
44
|
// The SAME assembly an embedder gets from `createAgentService` — channels, control plane,
|
|
72
45
|
// schedules, long connections. `dev` opens the directory itself only because its startup report
|
|
73
|
-
// prints the opened values before anything mounts.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
closeTimeoutMs: SHUTDOWN_GRACE_MS,
|
|
79
|
-
control: { tunnel: opts.tunnel ?? false, ...(host !== undefined ? { host } : {}) },
|
|
80
|
-
onChannelClosed: (name, error) => failStartup(new Error(`${name} ${error === undefined ? "closed unexpectedly" : `failed: ${String(error)}`}`)),
|
|
81
|
-
}).catch(failStartup);
|
|
82
|
-
serve(service.handler, { port: portFlag ?? a.config.http?.port ?? 8787, host }, {
|
|
83
|
-
ready: service.ready,
|
|
84
|
-
onListening: (p) => {
|
|
85
|
-
reportServing(service, host, p);
|
|
86
|
-
service.announce(p);
|
|
87
|
-
maybeTunnel(a.agentDir, service.channels.routes, p, opts.tunnel ?? false, a.stateRoot);
|
|
88
|
-
},
|
|
89
|
-
onShutdown: () => service.close(),
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
/** The agents/skills/tools/collisions report lines. */
|
|
93
|
-
function reportAgentsSkillsTools(a) {
|
|
94
|
-
reportLine("context", a.definition.contextFiles.map((f) => f.path).join(", ") || "(none)");
|
|
95
|
-
if (a.definition.persona)
|
|
96
|
-
reportLine("persona", "persona.md");
|
|
97
|
-
reportLine("skills", a.definition.skills.map((s) => s.name).join(", ") || "(none)");
|
|
98
|
-
reportLine("codingTools", CODING_TOOL_NAMES.join(", "));
|
|
99
|
-
if (a.toolNames.length > 0)
|
|
100
|
-
reportLine("tools", a.toolNames.join(", "));
|
|
101
|
-
if (a.deferredToolNames.length > 0) {
|
|
102
|
-
reportLine("deferred", `${a.deferredToolNames.join(", ")} (activated via search_tools)`);
|
|
103
|
-
}
|
|
104
|
-
reportToolCollisions(a.toolCollisions);
|
|
105
|
-
reportModuleLoadFailures(a.toolFailures);
|
|
106
|
-
reportFindingsIfChanged(a.definition.dir, a.definition);
|
|
46
|
+
// prints the opened values before anything mounts. The turn trace (tool calls + reply) logs at
|
|
47
|
+
// debug level: shown here, gated out in start (level info), keeping end-user content out of
|
|
48
|
+
// production logs.
|
|
49
|
+
const service = await mountAgentService(a, cliMountOptions(logAgentLoop)).catch(failStartup);
|
|
50
|
+
serveService(service, { port: portFlag ?? a.config.http?.port ?? 8787, host }, { tunnel, agentDir: a.agentDir, stateRoot: a.stateRoot });
|
|
107
51
|
}
|
|
@@ -4,23 +4,17 @@
|
|
|
4
4
|
* the schedule's stable session (faithful to the served behavior). Does NOT advance the schedule's fire
|
|
5
5
|
* state — a test run must never make the scheduler skip the real next run.
|
|
6
6
|
*/
|
|
7
|
-
import { join
|
|
8
|
-
import { loadDotEnv } from "../../env.js";
|
|
7
|
+
import { join } from "node:path";
|
|
9
8
|
import { displayPath } from "../../paths.js";
|
|
10
|
-
import { reportModuleLoadFailures } from "../../
|
|
9
|
+
import { reportModuleLoadFailures } from "../../loader.js";
|
|
11
10
|
import { createPiAgentFromDir } from "../../engines/pi/open.js";
|
|
12
11
|
import { runInvokeStream } from "../invoke-stream.js";
|
|
13
|
-
import { installProxyFetch } from "../../proxy.js";
|
|
14
12
|
import { loadSchedules } from "../../schedule/discover.js";
|
|
15
13
|
import { scheduleSession } from "../../schedule/scheduler.js";
|
|
16
|
-
import { failStartup
|
|
17
|
-
import {
|
|
14
|
+
import { failStartup } from "../fail.js";
|
|
15
|
+
import { enterAgentCommand, reportAuth } from "../shared.js";
|
|
18
16
|
export async function runFire(name, dirArg, opts) {
|
|
19
|
-
const
|
|
20
|
-
const placement = placementOrExit(fireDir);
|
|
21
|
-
loadDotEnv(placement.agentDir);
|
|
22
|
-
installProxyFetch();
|
|
23
|
-
await resolveFirstRunModel(placement.agentDir, opts);
|
|
17
|
+
const placement = await enterAgentCommand(dirArg, opts);
|
|
24
18
|
// Schedules are agent surface — discover them where dev/start/`schedule list` do (the agent
|
|
25
19
|
// dir), so `fire` sees the same set the scheduler serves.
|
|
26
20
|
const { schedules, failures } = await loadSchedules(placement.agentDir).catch(failStartup);
|
|
@@ -32,7 +26,7 @@ export async function runFire(name, dirArg, opts) {
|
|
|
32
26
|
failStartup(new Error(`unknown schedule "${name}" (looked in ${displayPath(process.cwd(), join(placement.agentDir, "schedules")) ?? "schedules"}). ` +
|
|
33
27
|
`available: ${schedules.map((s) => s.name).join(", ") || "(none)"}`));
|
|
34
28
|
}
|
|
35
|
-
const { agent, modelSpec, authPath } = await createPiAgentFromDir(
|
|
29
|
+
const { agent, modelSpec, authPath } = await createPiAgentFromDir(placement.workspace, {
|
|
36
30
|
model: opts.model,
|
|
37
31
|
authPath: opts.authPath, // flag > FASTAGENT_AUTH_PATH > default — resolved by the opener (one owner)
|
|
38
32
|
}).catch(failStartup);
|
|
@@ -8,8 +8,8 @@ import { resolveStateRoot, workspaceHint } from "../../paths.js";
|
|
|
8
8
|
import { CODING_TOOL_NAMES, resolveAgentTools } from "../../engines/pi/create.js";
|
|
9
9
|
import { loadAgentDefinition } from "../../engines/pi/definition.js";
|
|
10
10
|
import { reportFindingsIfChanged, reportToolCollisions } from "../../engines/pi/report.js";
|
|
11
|
-
import { reportModuleLoadFailures } from "../../log.js";
|
|
12
11
|
import { log } from "../../log.js";
|
|
12
|
+
import { reportModuleLoadFailures } from "../../loader.js";
|
|
13
13
|
import { nextRun } from "../../schedule/cron.js";
|
|
14
14
|
import { loadSchedules } from "../../schedule/discover.js";
|
|
15
15
|
import { failStartup, placementOrExit } from "../fail.js";
|
|
@@ -7,10 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
import { basename, join, resolve } from "node:path";
|
|
10
|
-
import { DEFAULT_AGENT_DIRNAME, SECRETS_DIRNAME, agentsAt } from "../../paths.js";
|
|
10
|
+
import { DEFAULT_AGENT_DIRNAME, SECRETS_DIRNAME, agentsAt, displayPath } from "../../paths.js";
|
|
11
11
|
import { detectRuntime, readPackageJson } from "../../runtime.js";
|
|
12
12
|
import { agentDirName, agentDirNameError, scaffoldAgent } from "../../scaffold/init.js";
|
|
13
|
-
import { displayPath } from "../../paths.js";
|
|
14
13
|
import { failStartup, failUsage } from "../fail.js";
|
|
15
14
|
export async function runInit(dirArg, opts) {
|
|
16
15
|
const dir = resolve(dirArg);
|
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
/** `fastagent invoke <message> [dir]`: run ONE turn against the assembled agent, then exit. */
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { resolve } from "node:path";
|
|
4
|
-
import { loadDotEnv } from "../../env.js";
|
|
5
3
|
import { createPiAgentFromDir } from "../../engines/pi/open.js";
|
|
6
4
|
import { runInvokeStream } from "../invoke-stream.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { reportAuth, resolveFirstRunModel } from "../shared.js";
|
|
5
|
+
import { failStartup } from "../fail.js";
|
|
6
|
+
import { enterAgentCommand, reportAuth } from "../shared.js";
|
|
10
7
|
export async function runInvoke(message, dirArg, opts) {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
loadDotEnv(placement.agentDir);
|
|
14
|
-
installProxyFetch();
|
|
15
|
-
await resolveFirstRunModel(placement.agentDir, opts);
|
|
16
|
-
const { agent, modelSpec, authPath } = await createPiAgentFromDir(invokeDir, {
|
|
8
|
+
const placement = await enterAgentCommand(dirArg, opts);
|
|
9
|
+
const { agent, modelSpec, authPath } = await createPiAgentFromDir(placement.workspace, {
|
|
17
10
|
model: opts.model,
|
|
18
11
|
authPath: opts.authPath, // flag > FASTAGENT_AUTH_PATH > default — resolved by the opener (one owner)
|
|
19
12
|
}).catch(failStartup);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
7
|
import { loadDotEnv } from "../../env.js";
|
|
8
8
|
import { resolveStateRoot } from "../../paths.js";
|
|
9
|
-
import { reportModuleLoadFailures } from "../../
|
|
9
|
+
import { reportModuleLoadFailures } from "../../loader.js";
|
|
10
10
|
import { readRuns } from "../../schedule/audit.js";
|
|
11
11
|
import { nextRun } from "../../schedule/cron.js";
|
|
12
12
|
import { loadSchedules } from "../../schedule/discover.js";
|
|
@@ -2,67 +2,49 @@
|
|
|
2
2
|
* `fastagent start [dir]`: run the agent in production posture — the SAME assembly as dev (your
|
|
3
3
|
* directory is the agent), just no file-watching. No build step: start reads the definition directly.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { authSeedBytes, collectAuthSeed } from "../../deploy/
|
|
8
|
-
import { loadDotEnv } from "../../env.js";
|
|
5
|
+
import { dirname } from "node:path";
|
|
6
|
+
import { writeFileAtomic } from "../../atomic-write.js";
|
|
7
|
+
import { authSeedBytes, collectAuthSeed } from "../../deploy/secrets.js";
|
|
9
8
|
import { resolveAuthPath, resolveSessionsDirOverride } from "../../engines/pi/config.js";
|
|
10
|
-
import { resolveSecretsDir,
|
|
11
|
-
import {
|
|
12
|
-
import { reportFindingsIfChanged, reportToolCollisions } from "../../engines/pi/report.js";
|
|
13
|
-
import { reportModuleLoadFailures } from "../../log.js";
|
|
14
|
-
import { CODING_TOOL_NAMES } from "../../engines/pi/create.js";
|
|
9
|
+
import { SECRET_FILE_MODE, ensureSecretsDir, resolveSecretsDir, isUnderDir, exists } from "../../paths.js";
|
|
10
|
+
import { log, setLogLevel } from "../../log.js";
|
|
15
11
|
import { createPiAgentFromDir } from "../../engines/pi/open.js";
|
|
16
12
|
import { mountAgentService } from "../../service.js";
|
|
17
|
-
import { log, setLogLevel } from "../../log.js";
|
|
18
13
|
import { logAgentLoop } from "../../observe.js";
|
|
19
|
-
import { installProxyFetch } from "../../proxy.js";
|
|
20
|
-
import { exists } from "../../paths.js";
|
|
21
|
-
import { bindAddress } from "../../bind.js";
|
|
22
14
|
import { isAgentcoreRuntime, mountAgentcoreService } from "../../channels/agentcore-service.js";
|
|
23
|
-
import { createWakeAlarmSink
|
|
15
|
+
import { createWakeAlarmSink } from "../../schedule/wake-alarm.js";
|
|
24
16
|
import { setWakeupsSink } from "../../schedule/wakeups.js";
|
|
25
|
-
import { failStartup
|
|
26
|
-
import {
|
|
27
|
-
import { parseBind, parsePort,
|
|
17
|
+
import { failStartup } from "../fail.js";
|
|
18
|
+
import { cliMountOptions, resolveBindHost, serveService } from "../serve.js";
|
|
19
|
+
import { enterAgentCommand, parseBind, parsePort, reportAssembly } from "../shared.js";
|
|
28
20
|
export async function runStart(dirArg, opts) {
|
|
29
|
-
const dir = resolve(dirArg);
|
|
30
21
|
// Flag validation first: a bad --port is a USAGE error (exit 2), and reporting it must not depend on
|
|
31
22
|
// the directory being an agent (which is a runtime/environment failure, exit 1).
|
|
32
23
|
const portFlag = parsePort(opts.port, "--port", "flag");
|
|
33
24
|
const bindFlag = parseBind(opts.bind);
|
|
34
|
-
const
|
|
25
|
+
const tunnel = opts.tunnel ?? false;
|
|
35
26
|
setLogLevel("info"); // production posture: info+, the debug turn trace (and its end-user content) gated out
|
|
36
|
-
|
|
37
|
-
installProxyFetch();
|
|
38
|
-
await resolveFirstRunModel(placement.agentDir, opts);
|
|
27
|
+
const placement = await enterAgentCommand(dirArg, opts);
|
|
39
28
|
// A `deploy --run` may carry the operator's local credential as FASTAGENT_AUTH_SEED —
|
|
40
29
|
// materialize it into the writable secrets dir BEFORE the opener resolves auth (once, absent-only).
|
|
41
30
|
// Same resolveAuthPath the opener uses — ONE owner of the flag > env > default chain.
|
|
42
31
|
await maybeSeedAuth(resolveAuthPath(placement.agentDir, opts.authPath));
|
|
43
32
|
// The same opener dev uses (single assembly source), just no watch.
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (deferredToolNames.length > 0) {
|
|
60
|
-
reportLine("deferred", `${deferredToolNames.join(", ")} (activated via search_tools)`);
|
|
61
|
-
}
|
|
62
|
-
reportToolCollisions(toolCollisions);
|
|
63
|
-
reportModuleLoadFailures(toolFailures);
|
|
64
|
-
reportLine("state", stateRoot);
|
|
65
|
-
reportLine("sessions", sessionsDir);
|
|
33
|
+
const opened = await createPiAgentFromDir(placement.workspace, {
|
|
34
|
+
model: opts.model,
|
|
35
|
+
sessionsDir: resolveSessionsDirOverride(opts.sessionsDir),
|
|
36
|
+
authPath: opts.authPath,
|
|
37
|
+
serving: true, // long-running serve: the scheduler poller runs (wake mounts iff config.selfSchedule)
|
|
38
|
+
}).catch(failStartup);
|
|
39
|
+
const { agent, agentDir, config, stateRoot, sessionsDir } = opened;
|
|
40
|
+
// The same report `dev` prints; `state:`/`sessions:` are start's own extras, and the persistence
|
|
41
|
+
// notes below are why (see reportAssembly on the asymmetry).
|
|
42
|
+
await reportAssembly(opened, {
|
|
43
|
+
afterTools: [
|
|
44
|
+
["state", stateRoot],
|
|
45
|
+
["sessions", sessionsDir],
|
|
46
|
+
],
|
|
47
|
+
});
|
|
66
48
|
// State defaults under the agent dir, which a redeploy may replace wholesale. Gate on where the
|
|
67
49
|
// state root ACTUALLY resolved (inside the agent dir?), not on the raw env var: an empty
|
|
68
50
|
// `FASTAGENT_STATE_DIR=""` reads as unset (resolveStateRoot) and still lands in-agent, so a raw
|
|
@@ -81,15 +63,9 @@ export async function runStart(dirArg, opts) {
|
|
|
81
63
|
log.info(`[fastagent] note: secrets (.env, rotated auth.json) live under the definition dir; point ` +
|
|
82
64
|
`FASTAGENT_SECRETS_DIR at a persistent volume so a redeploy that replaces the dir does not wipe them.`);
|
|
83
65
|
}
|
|
84
|
-
|
|
85
|
-
// Same debug turn trace as dev; gated out here by the info level (see dev.ts serveOnce).
|
|
66
|
+
// Same debug turn trace as dev; gated out here by the info level.
|
|
86
67
|
const traced = logAgentLoop(agent);
|
|
87
|
-
|
|
88
|
-
// configured `localhost` is an ADDRESS by the time anything binds, renders or dials it.
|
|
89
|
-
const configured = config.http?.host;
|
|
90
|
-
const host = bindFlag ?? (configured === undefined ? undefined : bindAddress(configured));
|
|
91
|
-
assertTunnelBindable(host, opts.tunnel ?? false, bindFlag ? "flag" : "config");
|
|
92
|
-
const control = { tunnel: opts.tunnel ?? false, ...(host !== undefined ? { host } : {}) };
|
|
68
|
+
const host = resolveBindHost(bindFlag, config.http?.host, tunnel);
|
|
93
69
|
// ONE branch for the whole posture. AgentCore assembles differently — lazy channels over a
|
|
94
70
|
// pre-restore state mount, an external clock, no resident connections — but it yields the same
|
|
95
71
|
// AgentService, so everything below this point is common.
|
|
@@ -98,22 +74,9 @@ export async function runStart(dirArg, opts) {
|
|
|
98
74
|
// handed to something that can, without inventing an ownership the singleton does not have.
|
|
99
75
|
const onStateReady = isAgentcoreRuntime() && config.selfSchedule ? armWakeAlarms(stateRoot) : undefined;
|
|
100
76
|
const service = await (isAgentcoreRuntime()
|
|
101
|
-
? mountAgentcoreService(opened, { wrapAgent: () => traced,
|
|
102
|
-
: mountAgentService(opened,
|
|
103
|
-
|
|
104
|
-
closeTimeoutMs: SHUTDOWN_GRACE_MS,
|
|
105
|
-
control,
|
|
106
|
-
onChannelClosed: (name, error) => failStartup(new Error(`${name} ${error === undefined ? "closed unexpectedly" : `failed: ${String(error)}`}`)),
|
|
107
|
-
})).catch(failStartup);
|
|
108
|
-
serve(service.handler, { port: portFlag ?? parsePort(process.env.PORT, "PORT env", "env") ?? config.http?.port ?? 8787, host }, {
|
|
109
|
-
ready: service.ready,
|
|
110
|
-
onListening: (p) => {
|
|
111
|
-
reportServing(service, host, p);
|
|
112
|
-
service.announce(p);
|
|
113
|
-
maybeTunnel(agentDir, service.channels.routes, p, opts.tunnel ?? false, stateRoot);
|
|
114
|
-
},
|
|
115
|
-
onShutdown: () => service.close(),
|
|
116
|
-
});
|
|
77
|
+
? mountAgentcoreService(opened, { wrapAgent: () => traced, onStateReady })
|
|
78
|
+
: mountAgentService(opened, cliMountOptions(() => traced))).catch(failStartup);
|
|
79
|
+
serveService(service, { port: portFlag ?? parsePort(process.env.PORT, "PORT env", "env") ?? config.http?.port ?? 8787, host }, { tunnel, agentDir, stateRoot });
|
|
117
80
|
// No graceful drain: webhook turns run fire-and-forget; SIGTERM just exits mid-turn. Whether an
|
|
118
81
|
// in-flight turn is LOST depends on the channel: the Telegram channel persists turn intent pre-ACK
|
|
119
82
|
// and replays it next start (turn-store.ts, L1 durable execution, at-least-once); HTTP and other
|
|
@@ -125,23 +88,19 @@ export async function runStart(dirArg, opts) {
|
|
|
125
88
|
* volume copy is never clobbered by the stale seed. Lets a deploy carry the operator's local
|
|
126
89
|
* OAuth/API credential so the box runs on the SAME subscription. No-op locally (the seed is unset).
|
|
127
90
|
*/
|
|
128
|
-
/** The opener, kept as its own call so `opened` can also feed the shared assembly below. */
|
|
129
|
-
function openStartDir(dir, opts, sessionsDir) {
|
|
130
|
-
return createPiAgentFromDir(dir, {
|
|
131
|
-
model: opts.model,
|
|
132
|
-
sessionsDir,
|
|
133
|
-
authPath: opts.authPath,
|
|
134
|
-
serving: true, // long-running serve: the scheduler poller runs (wake mounts iff config.selfSchedule)
|
|
135
|
-
}).catch(failStartup);
|
|
136
|
-
}
|
|
137
91
|
async function maybeSeedAuth(authPath) {
|
|
138
92
|
// collectAuthSeed: the seed may arrive CHUNKED (FASTAGENT_AUTH_SEED + _2…) on hosts with a small
|
|
139
93
|
// env-value max length (AgentCore); single-var hosts are unchanged.
|
|
140
94
|
const bytes = authSeedBytes(collectAuthSeed(process.env), await exists(authPath));
|
|
141
95
|
if (!bytes)
|
|
142
96
|
return;
|
|
143
|
-
await
|
|
144
|
-
|
|
97
|
+
await ensureSecretsDir(dirname(authPath));
|
|
98
|
+
// Same spelling as the credential store: the mode is applied before the content is reachable, so
|
|
99
|
+
// the seed can never be published at some other file's looser permissions. It does NOT close the
|
|
100
|
+
// gap between the `exists` check and the write — the rename would overwrite a file created in
|
|
101
|
+
// between, against this function's absent-only promise. That window needs two `start` processes on
|
|
102
|
+
// one secrets dir, which no supported deployment runs (one container, one volume).
|
|
103
|
+
writeFileAtomic(authPath, bytes, SECRET_FILE_MODE);
|
|
145
104
|
log.info(`[fastagent] seeded ${authPath} from FASTAGENT_AUTH_SEED (first boot)`);
|
|
146
105
|
}
|
|
147
106
|
/**
|
|
@@ -160,5 +119,7 @@ function armWakeAlarms(stateRoot) {
|
|
|
160
119
|
const sink = createWakeAlarmSink({ secret });
|
|
161
120
|
setWakeupsSink(sink);
|
|
162
121
|
log.info(`[fastagent] wake alarms: EventBridge-backed via the forwarder`);
|
|
163
|
-
|
|
122
|
+
// Boot reconcile: pending wake-ups may exist while their alarms were lost (a deploy replaced the
|
|
123
|
+
// forwarder, a sink call failed). The sink re-reads the store, so a bare notification is enough.
|
|
124
|
+
return () => sink(stateRoot);
|
|
164
125
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/** `fastagent tool <name> '<json>' [dir]`: run one tool's body directly with JSON args — no model. */
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { NodeExecutionEnv } from "@earendil-works/pi-agent-core/node";
|
|
4
3
|
import { loadDotEnv } from "../../env.js";
|
|
5
4
|
import { loadConfig } from "../../engines/pi/config.js";
|
|
6
5
|
import { resolveAgentTools } from "../../engines/pi/create.js";
|
|
7
|
-
import { reportModuleLoadFailures } from "../../
|
|
6
|
+
import { reportModuleLoadFailures } from "../../loader.js";
|
|
8
7
|
import { turnContext } from "../../engines/pi/tool-context.js";
|
|
9
8
|
import { failStartup, failUsage, placementOrExit } from "../fail.js";
|
|
10
9
|
export async function runTool(name, argsJson, dirArg) {
|
|
@@ -26,12 +25,8 @@ export async function runTool(name, argsJson, dirArg) {
|
|
|
26
25
|
if (!tool) {
|
|
27
26
|
failStartup(new Error(`unknown tool "${name}". available: ${tools.map((t) => t.name).join(", ") || "(none)"}`));
|
|
28
27
|
}
|
|
29
|
-
// Authored tools read cwd from turnContext; coding tools are already rooted at the workspace.
|
|
30
|
-
|
|
31
|
-
const env = new NodeExecutionEnv({ cwd: workspace });
|
|
32
|
-
const result = await turnContext
|
|
33
|
-
.run({ cwd: workspace }, () => tool.execute(`cli-${name}`, args, undefined, undefined, { env }))
|
|
34
|
-
.catch(failStartup);
|
|
28
|
+
// Authored tools read cwd from turnContext; coding tools are already rooted at the workspace.
|
|
29
|
+
const result = await turnContext.run({ cwd: workspace }, () => tool.execute(`cli-${name}`, args)).catch(failStartup);
|
|
35
30
|
const out = result?.details !== undefined
|
|
36
31
|
? result.details
|
|
37
32
|
: (result?.content ?? []).map((c) => ("text" in c ? c.text : "")).join("");
|
package/dist/cli/kernel.d.ts
CHANGED
|
@@ -24,8 +24,6 @@ interface ArgSpec {
|
|
|
24
24
|
export interface FlagSpec {
|
|
25
25
|
flags: string;
|
|
26
26
|
description: string;
|
|
27
|
-
/** Parses but does not appear in help — for retired flags that should still explain themselves. */
|
|
28
|
-
hidden?: boolean;
|
|
29
27
|
/** Mutually exclusive with these {@link optionKey} values — validated at build time. */
|
|
30
28
|
conflicts?: string[];
|
|
31
29
|
}
|
package/dist/cli/kernel.js
CHANGED
package/dist/cli/program.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { fastagentVersion } from "../version.js";
|
|
8
8
|
import { buildProgram } from "./kernel.js";
|
|
9
|
+
import { DEPLOY_HOSTS } from "../deploy/hosts.js";
|
|
9
10
|
// Help groups (clig: most common commands first) — the authoring loop leads, operations close.
|
|
10
11
|
// Shared flags — same name, same meaning, on every command that supports them (clig: consistency).
|
|
11
12
|
const DIR_ARG = {
|
|
@@ -256,8 +257,6 @@ const start = {
|
|
|
256
257
|
input: f.input !== false,
|
|
257
258
|
}),
|
|
258
259
|
};
|
|
259
|
-
/** The retired app-creation flag — parsed so it can explain itself, hidden from help. */
|
|
260
|
-
const CREATE_APP = { flags: "--create-app", description: "(retired)", hidden: true };
|
|
261
260
|
const INGRESS = {
|
|
262
261
|
flags: "--ingress <mode>",
|
|
263
262
|
description: "Feishu/Lark ingress: websocket or webhook (interactive when omitted)",
|
|
@@ -280,18 +279,14 @@ const channelSub = (kind, summary, description, notes) => ({
|
|
|
280
279
|
summary,
|
|
281
280
|
description,
|
|
282
281
|
args: [DIR_ARG],
|
|
283
|
-
flags:
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
? [
|
|
287
|
-
:
|
|
288
|
-
? [GROUP_BEHAVIOR, NO_ONBOARD, REPLACE_CONFIG]
|
|
289
|
-
: []),
|
|
290
|
-
],
|
|
282
|
+
flags: kind === "feishu" || kind === "lark"
|
|
283
|
+
? [INGRESS, GROUP_BEHAVIOR]
|
|
284
|
+
: kind === "slack"
|
|
285
|
+
? [GROUP_BEHAVIOR, NO_ONBOARD, REPLACE_CONFIG]
|
|
286
|
+
: [],
|
|
291
287
|
examples: [{ cmd: `fastagent add ${kind}` }],
|
|
292
288
|
...(notes ? { notes } : {}),
|
|
293
289
|
run: async (args, f) => (await import("./commands/add.js")).runAddChannel(kind, args[0], {
|
|
294
|
-
createApp: f.createApp === true,
|
|
295
290
|
ingress: f.ingress,
|
|
296
291
|
groupBehavior: f.groupBehavior,
|
|
297
292
|
onboard: f.onboard !== false,
|
|
@@ -357,7 +352,7 @@ const deploy = {
|
|
|
357
352
|
"CloudFormation stack (AWS Bedrock AgentCore Runtime + forwarder Lambda for webhooks + " +
|
|
358
353
|
"EventBridge rules for schedules; linux/arm64 image built locally). Durable ingress " +
|
|
359
354
|
"remains operator-owned (agentcore's forwarder URL is the exception — the stack owns it).",
|
|
360
|
-
args: [{ name: "<host>", description: "deploy target", choices: [
|
|
355
|
+
args: [{ name: "<host>", description: "deploy target", choices: [...DEPLOY_HOSTS] }, DIR_ARG],
|
|
361
356
|
flags: [
|
|
362
357
|
{
|
|
363
358
|
flags: "--run",
|
package/dist/cli/serve.d.ts
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { AgentService } from "../service.ts";
|
|
3
|
-
/**
|
|
4
|
-
* Mount the session control plane (`/control/*`) when the agent enabled it
|
|
5
|
-
* (`config.sessionControl`): merge the bearer-authenticated routes and return an announcer that
|
|
6
|
-
* writes `<stateRoot>/control.json` — `{ url, token }`, 0600 — once the port is known. The file is
|
|
7
|
-
* the LOCAL discovery channel (`fastagent attach`, a local desktop app); filesystem permissions are
|
|
8
|
-
* its trust boundary, and each boot overwrites it with a fresh per-boot token. A user channel
|
|
9
|
-
* colliding on `/control/*` fails startup — the same disposition as a channel-channel collision
|
|
10
|
-
* (routesFor): `sessionControl` is an explicit opt-in, so declaring both is a configuration error,
|
|
11
|
-
* and silently shadowing either side would serve a surface the author didn't write.
|
|
12
|
-
*/
|
|
1
|
+
import type { Agent } from "../agent.ts";
|
|
2
|
+
import type { AgentService, MountAgentServiceOptions } from "../service.ts";
|
|
13
3
|
/**
|
|
14
4
|
* Refuse `--tunnel` with a bind that cloudflared cannot reach: it dials the NAME `localhost:<port>`
|
|
15
5
|
* (the dev supervisor's tunnel too), so anything outside `127.0.0.1`/`::1`/wildcard — including a
|
|
@@ -20,6 +10,32 @@ import type { AgentService } from "../service.ts";
|
|
|
20
10
|
* came from config is broken runtime configuration (1).
|
|
21
11
|
*/
|
|
22
12
|
export declare function assertTunnelBindable(host: string | undefined, tunnel: boolean, source: "flag" | "config"): void;
|
|
13
|
+
/**
|
|
14
|
+
* The bind address a serve uses: the flag, else `http.host` from the config — through `bindAddress`,
|
|
15
|
+
* so a configured `localhost` is an ADDRESS by the time anything binds, renders or dials it — checked
|
|
16
|
+
* against `--tunnel` with the exit code the SOURCE of the value earns (serve.ts assertTunnelBindable).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveBindHost(bindFlag: string | undefined, configured: string | undefined, tunnel: boolean): string | undefined;
|
|
19
|
+
/** What the CLI adds to the assembly: its shutdown grace, and exit on a connection that drops. */
|
|
20
|
+
export declare function cliMountOptions(wrapAgent: (agent: Agent) => Agent): MountAgentServiceOptions;
|
|
21
|
+
/**
|
|
22
|
+
* Bind, report, announce the control plane, open the tunnel, and close in order on a signal — the
|
|
23
|
+
* tail dev's worker and start share once the service is assembled.
|
|
24
|
+
*/
|
|
25
|
+
export declare function serveService(service: AgentService, bind: {
|
|
26
|
+
port: number;
|
|
27
|
+
host?: string;
|
|
28
|
+
}, posture: {
|
|
29
|
+
tunnel: boolean;
|
|
30
|
+
agentDir: string;
|
|
31
|
+
stateRoot: string;
|
|
32
|
+
}): void;
|
|
33
|
+
/**
|
|
34
|
+
* The "we are serving" report: the supervisor message `dev`'s watcher waits for, the addresses, and
|
|
35
|
+
* what mounted. One function because both commands must say the same thing at the same moment —
|
|
36
|
+
* after readiness, never at socket bind.
|
|
37
|
+
*/
|
|
38
|
+
export declare function reportServing(service: AgentService, host: string | undefined, boundPort: number): void;
|
|
23
39
|
/**
|
|
24
40
|
* The startup lines that name WHERE the serve is: the bind report, and the curl the reader copies.
|
|
25
41
|
* ONE function because they are one message — they were two, and `--bind` updated the first while the
|
|
@@ -29,29 +45,21 @@ export declare function assertTunnelBindable(host: string | undefined, tunnel: b
|
|
|
29
45
|
* A wildcard bind is every interface, and naming one address there would understate it — but the curl
|
|
30
46
|
* still needs one to dial, which is what `clientHost` gives (loopback for a wildcard, itself otherwise).
|
|
31
47
|
*/
|
|
32
|
-
/**
|
|
33
|
-
* The "we are serving" report: the supervisor message `dev`'s watcher waits for, the addresses, and
|
|
34
|
-
* what mounted. One function because both commands must say the same thing at the same moment —
|
|
35
|
-
* after readiness, never at socket bind.
|
|
36
|
-
*/
|
|
37
|
-
export declare function reportServing(service: AgentService, host: string | undefined, boundPort: number): void;
|
|
38
48
|
export declare function readyAddressLines(host: string | undefined, boundPort: number, builtinInvoke: boolean): string[];
|
|
39
49
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
50
|
+
* Write `<stateRoot>/control.json` so a LOCAL client (`fastagent attach`) can find the control plane
|
|
51
|
+
* once the port is known, and say what reaches it. Returns the file's removal — wired into the
|
|
52
|
+
* shutdown so a stale file cannot point a client at a dead port (`attach` then fails with "cannot
|
|
53
|
+
* read", accurate, instead of a stale token's misleading 401/ECONNREFUSED). A hard exit leaves the
|
|
54
|
+
* file behind: advisory, overwritten by the next boot.
|
|
55
|
+
*
|
|
56
|
+
* Here and not in the assembly: the file, its permissions and the warnings are how THIS process's
|
|
57
|
+
* operator finds and protects the plane. An embedder distributes `service.control` itself.
|
|
43
58
|
*/
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
port: number;
|
|
59
|
+
export declare function announceControl(control: {
|
|
60
|
+
token: string;
|
|
61
|
+
prefix: string;
|
|
62
|
+
} | undefined, stateRoot: string, bind: {
|
|
49
63
|
host?: string;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
ready?: Promise<void>;
|
|
53
|
-
onListening?: (boundPort: number) => void;
|
|
54
|
-
onShutdown?: () => Promise<void> | void;
|
|
55
|
-
}): void;
|
|
56
|
-
/** Start a Cloudflare tunnel for route channels only. */
|
|
57
|
-
export declare function maybeTunnel(agentDir: string, routeChannels: string[], boundPort: number, tunnel: boolean, stateRoot?: string): void;
|
|
64
|
+
tunnel: boolean;
|
|
65
|
+
}, boundPort: number): () => void;
|