@fastagent-sh/fastagent 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -4
- package/dist/bind.d.ts +34 -0
- package/dist/bind.js +74 -0
- package/dist/channels/agentcore-limits.d.ts +7 -0
- package/dist/channels/agentcore-limits.js +9 -0
- package/dist/channels/agentcore-state.d.ts +83 -0
- package/dist/channels/agentcore-state.js +266 -0
- package/dist/channels/agentcore.d.ts +98 -0
- package/dist/channels/agentcore.js +260 -0
- package/dist/channels/busy.d.ts +23 -0
- package/dist/channels/busy.js +53 -0
- package/dist/channels/context-buffer.d.ts +5 -0
- package/dist/channels/context-buffer.js +5 -0
- package/dist/channels/feishu/context-buffer.d.ts +13 -8
- package/dist/channels/feishu/context-buffer.js +60 -6
- package/dist/channels/feishu/feishu-api.js +4 -1
- package/dist/channels/feishu/feishu.d.ts +0 -12
- package/dist/channels/feishu/feishu.js +143 -54
- package/dist/channels/feishu/invoke-turn.js +35 -18
- package/dist/channels/feishu/model.d.ts +0 -1
- package/dist/channels/feishu/normalize.js +0 -1
- package/dist/channels/feishu/parse.d.ts +21 -7
- package/dist/channels/feishu/parse.js +24 -7
- package/dist/channels/feishu/preview.js +3 -2
- package/dist/channels/feishu/scaffold/channel.ts +9 -8
- package/dist/channels/feishu/scaffold/feishu-send.ts +6 -4
- package/dist/channels/feishu/setup-mode.d.ts +30 -1
- package/dist/channels/feishu/setup-mode.js +27 -1
- package/dist/channels/github/github.js +8 -1
- package/dist/channels/http.js +1 -1
- package/dist/channels/lark/scaffold/channel.ts +9 -8
- package/dist/channels/lark/scaffold/lark-send.ts +6 -4
- package/dist/channels/preview-kit.d.ts +7 -1
- package/dist/channels/preview-kit.js +3 -2
- package/dist/channels/slack/parse.d.ts +16 -1
- package/dist/channels/slack/parse.js +46 -3
- package/dist/channels/slack/preview.d.ts +1 -2
- package/dist/channels/slack/preview.js +68 -24
- package/dist/channels/slack/scaffold/channel.ts +5 -5
- package/dist/channels/slack/slack-api.d.ts +3 -23
- package/dist/channels/slack/slack-api.js +6 -22
- package/dist/channels/slack/slack.d.ts +13 -20
- package/dist/channels/slack/slack.js +95 -50
- package/dist/channels/state.d.ts +11 -4
- package/dist/channels/state.js +19 -12
- package/dist/channels/tasks.d.ts +0 -6
- package/dist/channels/tasks.js +16 -1
- package/dist/channels/telegram/parse.d.ts +0 -7
- package/dist/channels/telegram/parse.js +4 -2
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -3
- package/dist/channels/telegram/telegram.js +1 -1
- package/dist/channels/text.d.ts +14 -0
- package/dist/channels/text.js +14 -0
- package/dist/channels/thread-participants.d.ts +21 -0
- package/dist/channels/thread-participants.js +132 -0
- package/dist/channels/turn-queue.js +7 -0
- package/dist/cli/add-feishu.d.ts +7 -4
- package/dist/cli/add-feishu.js +57 -37
- package/dist/cli/add-slack.d.ts +2 -1
- package/dist/cli/add-slack.js +6 -11
- package/dist/cli/commands/add.js +50 -51
- package/dist/cli/commands/attach.js +8 -4
- package/dist/cli/commands/chat.js +8 -8
- package/dist/cli/commands/deploy.d.ts +14 -1
- package/dist/cli/commands/deploy.js +330 -78
- package/dist/cli/commands/dev.d.ts +1 -0
- package/dist/cli/commands/dev.js +38 -25
- package/dist/cli/commands/fire.js +15 -16
- package/dist/cli/commands/info.js +36 -29
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.js +65 -53
- package/dist/cli/commands/invoke.js +9 -6
- package/dist/cli/commands/login.js +35 -21
- package/dist/cli/commands/schedule.js +6 -8
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.js +101 -37
- package/dist/cli/commands/tool.js +30 -18
- package/dist/cli/fail.d.ts +17 -0
- package/dist/cli/fail.js +24 -0
- package/dist/cli/program.js +66 -36
- package/dist/cli/serve.d.ts +52 -8
- package/dist/cli/serve.js +133 -32
- package/dist/cli/shared.d.ts +21 -2
- package/dist/cli/shared.js +44 -18
- package/dist/deploy/agentcore/plan.d.ts +117 -0
- package/dist/deploy/agentcore/plan.js +721 -0
- package/dist/deploy/agentcore/run.d.ts +73 -0
- package/dist/deploy/agentcore/run.js +412 -0
- package/dist/deploy/agentcore/zip.d.ts +17 -0
- package/dist/deploy/agentcore/zip.js +68 -0
- package/dist/deploy/container.d.ts +26 -25
- package/dist/deploy/container.js +93 -89
- package/dist/deploy/docker/plan.d.ts +1 -1
- package/dist/deploy/docker/plan.js +12 -17
- package/dist/deploy/fly/plan.d.ts +2 -0
- package/dist/deploy/fly/plan.js +27 -19
- package/dist/deploy/fly/run.d.ts +12 -1
- package/dist/deploy/fly/run.js +36 -2
- package/dist/deploy/preflight.d.ts +11 -5
- package/dist/deploy/preflight.js +235 -65
- package/dist/deploy/railway/plan.d.ts +7 -0
- package/dist/deploy/railway/plan.js +41 -16
- package/dist/deploy/railway/run.d.ts +8 -1
- package/dist/deploy/railway/run.js +7 -2
- package/dist/deploy/runner.d.ts +5 -2
- package/dist/deploy/runner.js +9 -3
- package/dist/dev-supervisor.d.ts +11 -8
- package/dist/dev-supervisor.js +53 -51
- package/dist/engines/pi/auth.d.ts +8 -7
- package/dist/engines/pi/auth.js +12 -10
- package/dist/engines/pi/channel.d.ts +1 -1
- package/dist/engines/pi/channel.js +5 -5
- package/dist/engines/pi/chat.js +2 -2
- package/dist/engines/pi/config.d.ts +12 -48
- package/dist/engines/pi/config.js +29 -110
- package/dist/engines/pi/create.d.ts +32 -24
- package/dist/engines/pi/create.js +47 -23
- package/dist/engines/pi/definition.d.ts +7 -26
- package/dist/engines/pi/definition.js +8 -54
- package/dist/engines/pi/harness.d.ts +19 -5
- package/dist/engines/pi/harness.js +3 -5
- package/dist/engines/pi/login.d.ts +1 -1
- package/dist/engines/pi/models.d.ts +3 -3
- package/dist/engines/pi/models.js +1 -1
- package/dist/engines/pi/{workspace.d.ts → open.d.ts} +31 -25
- package/dist/engines/pi/{workspace.js → open.js} +27 -29
- package/dist/engines/pi/read-image.d.ts +4 -0
- package/dist/engines/pi/read-image.js +62 -0
- package/dist/engines/pi/search-tools.d.ts +6 -4
- package/dist/engines/pi/search-tools.js +3 -1
- package/dist/engines/pi/session-builder.d.ts +2 -2
- package/dist/engines/pi/session-builder.js +18 -13
- package/dist/engines/pi/tool.d.ts +13 -5
- package/dist/engines/pi/tool.js +4 -0
- package/dist/engines/pi/wake-tool.d.ts +3 -3
- package/dist/env.d.ts +16 -4
- package/dist/env.js +43 -5
- package/dist/host/node.d.ts +4 -2
- package/dist/host/node.js +2 -1
- package/dist/loader.d.ts +2 -2
- package/dist/loader.js +3 -3
- package/dist/log.d.ts +1 -1
- package/dist/log.js +1 -1
- package/dist/paths.d.ts +138 -0
- package/dist/paths.js +326 -0
- package/dist/pi.d.ts +2 -2
- package/dist/pi.js +2 -2
- package/dist/runtime.d.ts +7 -5
- package/dist/runtime.js +2 -2
- package/dist/scaffold/add-channel.d.ts +7 -3
- package/dist/scaffold/add-channel.js +55 -29
- package/dist/scaffold/init.d.ts +32 -41
- package/dist/scaffold/init.js +161 -185
- package/dist/scaffold/templates/env.example +15 -6
- package/dist/scaffold/templates/fastagent.config.mjs +1 -1
- package/dist/scaffold/templates/gitignore +14 -6
- package/dist/scaffold/templates/persona.md +4 -2
- package/dist/scaffold/templates/secrets.gitignore +5 -0
- package/dist/scaffold/templates.d.ts +1 -7
- package/dist/scaffold/templates.js +3 -25
- package/dist/scaffold/vendor-skill.d.ts +2 -2
- package/dist/scaffold/vendor-skill.js +13 -13
- package/dist/schedule/discover.js +4 -4
- package/dist/schedule/scheduler.d.ts +40 -1
- package/dist/schedule/scheduler.js +89 -56
- package/dist/schedule/state.js +1 -1
- package/dist/schedule/wake-alarm.d.ts +47 -0
- package/dist/schedule/wake-alarm.js +136 -0
- package/dist/schedule/wakeups.d.ts +1 -0
- package/dist/schedule/wakeups.js +18 -0
- package/dist/tunnel.d.ts +3 -3
- package/dist/tunnel.js +7 -7
- package/package.json +7 -4
- package/dist/channels/feishu/owned-threads.d.ts +0 -7
- package/dist/channels/feishu/owned-threads.js +0 -47
- package/dist/channels/slack/owned-threads.d.ts +0 -6
- package/dist/channels/slack/owned-threads.js +0 -43
- package/dist/scaffold/templates/gitignore.agentdir-root +0 -5
- package/dist/scaffold/templates/gitignore.kit +0 -2
- package/dist/workspace.d.ts +0 -9
- package/dist/workspace.js +0 -45
package/dist/cli/commands/add.js
CHANGED
|
@@ -7,21 +7,36 @@ import { readFile } from "node:fs/promises";
|
|
|
7
7
|
import { join, relative, resolve } from "node:path";
|
|
8
8
|
import { isCancel, select } from "@clack/prompts";
|
|
9
9
|
import { onboardFeishuCloudApp } from "../add-feishu.js";
|
|
10
|
-
import { loadDotEnv } from "../../env.js";
|
|
11
|
-
import {
|
|
10
|
+
import { dotEnvPath, loadDotEnv } from "../../env.js";
|
|
11
|
+
import { resolveStateRoot } from "../../paths.js";
|
|
12
|
+
import { SECRETS_DIRNAME } from "../../paths.js";
|
|
12
13
|
import { detectRuntime, readPackageJson } from "../../runtime.js";
|
|
14
|
+
import { isUnderDir } from "../../engines/pi/definition.js";
|
|
15
|
+
import { displayPath } from "../../paths.js";
|
|
13
16
|
import { appendChannelDotEnv, appendChannelEnv, assertChannelReady, channelExists, channelSetup, scaffoldChannel, } from "../../scaffold/add-channel.js";
|
|
14
|
-
import { exists } from "../../
|
|
17
|
+
import { exists } from "../../paths.js";
|
|
15
18
|
import { vendorSkill } from "../../scaffold/vendor-skill.js";
|
|
16
|
-
import {
|
|
17
|
-
import { failStartup, failUsage } from "../fail.js";
|
|
19
|
+
import { failStartup, failUsage, placementOrExit } from "../fail.js";
|
|
18
20
|
/** `fastagent add <kind> [dir]`: scaffold `channels/<kind>.ts` — the adapter import plus a starter `on()`. */
|
|
19
21
|
export async function runAddChannel(channelKind, dirArg, opts) {
|
|
20
|
-
|
|
22
|
+
// The channel (glue + companion tool + secrets) is agent surface — everything lands in the
|
|
23
|
+
// AGENT DIR (`fastagent/`), the same place dev/start discover channels/.
|
|
24
|
+
// Flag conflicts first: a bad combination is a USAGE error (exit 2), and reporting it must not depend
|
|
25
|
+
// on the directory being an agent (a runtime/environment failure, exit 1) — the same order start/tool
|
|
26
|
+
// follow.
|
|
21
27
|
if (opts.replaceConfig && opts.onboard === false) {
|
|
22
28
|
failUsage("--replace-config replaces onboarding credentials; it cannot be combined with --no-onboard");
|
|
23
29
|
}
|
|
30
|
+
const { agentDir: target } = placementOrExit(resolve(dirArg));
|
|
24
31
|
loadDotEnv(target); // onboarding state follows the same FASTAGENT_STATE_DIR as serving/deploy
|
|
32
|
+
// Paths are printed to someone standing in their CWD, usually the workspace, while every file
|
|
33
|
+
// belongs to the AGENT dir — prefix them, or they point at nothing. `displayPath` owns the
|
|
34
|
+
// relative-vs-absolute policy (shared with `init`). The `.env` label names the file actually
|
|
35
|
+
// WRITTEN (FASTAGENT_SECRETS_DIR relocates it, possibly out of the agent), never the default spelling.
|
|
36
|
+
const agentFromCwd = displayPath(process.cwd(), target);
|
|
37
|
+
const inAgent = (p) => (agentFromCwd === undefined ? p : join(agentFromCwd, p));
|
|
38
|
+
const envPath = dotEnvPath(target);
|
|
39
|
+
const envLabel = isUnderDir(envPath, target) ? inAgent(relative(target, envPath)) : envPath;
|
|
25
40
|
// App creation is not a flag — it is what `add feishu` IS (the scan-to-create flow is the default
|
|
26
41
|
// and only path there). The retired --create-app spelling gets a pointer, not silence.
|
|
27
42
|
if (opts.createApp) {
|
|
@@ -35,18 +50,13 @@ export async function runAddChannel(channelKind, dirArg, opts) {
|
|
|
35
50
|
failStartup(new Error("--create-app is retired — app creation is the default behavior of `add feishu`"));
|
|
36
51
|
}
|
|
37
52
|
}
|
|
38
|
-
// The channel (glue + companion tool) is agent surface — it lands in agentDir (config.agentDir, or
|
|
39
|
-
// target when flat), the same place dev/start discover channels/. .env(.example) and the secret
|
|
40
|
-
// hygiene stay at the run root, where .env is actually read.
|
|
41
|
-
const { config: addConfig } = await loadConfig(target).catch(failStartup);
|
|
42
|
-
const channelHome = resolveAgentDir(target, addConfig);
|
|
43
53
|
// Preconditions before the write, so a refusal is side-effect-free. slack/feishu/lark are exceptions:
|
|
44
54
|
// their add is scaffold + ONBOARD THE APP, so an existing scaffold skips the write and continues (a
|
|
45
55
|
// failed/cancelled app or OAuth flow must be re-runnable without hand-deleting authored glue).
|
|
46
|
-
const file = join(
|
|
47
|
-
const slackToolFile = join(
|
|
56
|
+
const file = join(target, "channels", `${channelKind}.ts`);
|
|
57
|
+
const slackToolFile = join(target, "tools", "slack-send.ts");
|
|
48
58
|
const slackToolExisted = channelKind === "slack" ? await exists(slackToolFile) : false;
|
|
49
|
-
const existsAlready = await channelExists(
|
|
59
|
+
const existsAlready = await channelExists(target, channelKind).catch(failStartup);
|
|
50
60
|
const ingress = await resolveIngress(channelKind, file, existsAlready, opts.ingress);
|
|
51
61
|
const groupBehavior = await resolveGroupBehavior(channelKind, opts.groupBehavior);
|
|
52
62
|
if (existsAlready) {
|
|
@@ -56,23 +66,15 @@ export async function runAddChannel(channelKind, dirArg, opts) {
|
|
|
56
66
|
console.error(`[fastagent] ${relative(target, file)} already exists — keeping it`);
|
|
57
67
|
}
|
|
58
68
|
else {
|
|
59
|
-
await assertChannelReady(
|
|
60
|
-
await scaffoldChannel(
|
|
69
|
+
await assertChannelReady(target).catch(failStartup);
|
|
70
|
+
await scaffoldChannel(target, channelKind, { ingress, groupBehavior: groupBehavior.behavior }).catch(failStartup);
|
|
61
71
|
console.error(`[fastagent] created ${relative(target, file)}`);
|
|
62
72
|
if (channelKind === "slack") {
|
|
63
73
|
console.error(`[fastagent] ${slackToolExisted ? "kept existing" : "created"} ${relative(target, slackToolFile)}`);
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
76
|
if (await appendChannelEnv(target, channelKind, ingress).catch(failStartup)) {
|
|
67
|
-
console.error(`[fastagent] added ${channelKind} env vars to .env.example`);
|
|
68
|
-
}
|
|
69
|
-
// Secret hygiene: a channel's GENERATED secret (a random string the user contributes nothing to) is
|
|
70
|
-
// written into `.env` — but only when `.env` is already gitignored: the CLI must never materialize a
|
|
71
|
-
// secret into a committable file. Warn, not refuse, when it is exposed — channel glue may read a real
|
|
72
|
-
// env var instead.
|
|
73
|
-
const envIgnored = (await loadRootIgnore(target).catch(failStartup))?.ignores(".env") ?? false;
|
|
74
|
-
if (!envIgnored) {
|
|
75
|
-
console.error(`[fastagent] warn: .env is not gitignored — a deploy that copies the directory would ship a secret placed there; add .env to .gitignore/.fastagentignore, or use a real env var`);
|
|
77
|
+
console.error(`[fastagent] added ${channelKind} env vars to ${inAgent(join(SECRETS_DIRNAME, ".env.example"))}`);
|
|
76
78
|
}
|
|
77
79
|
// Stateful app onboarding is re-runnable after the scaffold boundary. Slack's internal-app path
|
|
78
80
|
// persists its manifest/OAuth recovery state separately and writes runtime secrets directly.
|
|
@@ -82,7 +84,6 @@ export async function runAddChannel(channelKind, dirArg, opts) {
|
|
|
82
84
|
created = await onboardSlackInternalApp({
|
|
83
85
|
target,
|
|
84
86
|
stateRoot: resolveStateRoot(target),
|
|
85
|
-
envIgnored,
|
|
86
87
|
groupBehavior,
|
|
87
88
|
replaceConfig: opts.replaceConfig,
|
|
88
89
|
})
|
|
@@ -90,13 +91,13 @@ export async function runAddChannel(channelKind, dirArg, opts) {
|
|
|
90
91
|
.catch(failStartup);
|
|
91
92
|
}
|
|
92
93
|
else if (channelKind === "feishu" || channelKind === "lark") {
|
|
93
|
-
created = await onboardFeishuCloudApp(target, channelKind,
|
|
94
|
+
created = await onboardFeishuCloudApp(target, channelKind, ingress, groupBehavior).catch(failStartup);
|
|
94
95
|
}
|
|
95
96
|
const setup = channelSetup(channelKind, ingress, groupBehavior.behavior);
|
|
96
97
|
const env = setup.env;
|
|
97
98
|
const steps = channelKind === "slack" && opts.onboard !== false
|
|
98
99
|
? [
|
|
99
|
-
|
|
100
|
+
`Slack internal app created/configured/installed through OAuth; runtime credentials are in ${envLabel}`,
|
|
100
101
|
"run fastagent dev --tunnel to replace the temporary Events API URL automatically",
|
|
101
102
|
"invite the app to every channel it should read",
|
|
102
103
|
"the agent can send messages or files by calling the scaffolded {tools}/slack-send.ts tool",
|
|
@@ -106,35 +107,35 @@ export async function runAddChannel(channelKind, dirArg, opts) {
|
|
|
106
107
|
// Kind-neutral: every channel's generated secrets get the same treatment (github's webhook secret is
|
|
107
108
|
// the same class of value as telegram's); guided Lark credentials ride the same write as overwrites.
|
|
108
109
|
// Feishu's irreversible credentials were already staged inside add-feishu.ts before bootstrap.
|
|
109
|
-
const dotEnv =
|
|
110
|
-
|
|
111
|
-
:
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
const dotEnv = await appendChannelDotEnv(target, channelKind, { ...generated, ...created }, Object.keys(created ?? {}), ingress).catch(failStartup);
|
|
111
|
+
if (dotEnv.unprotectedSecretsDir) {
|
|
112
|
+
console.error(`[fastagent] note: ${dotEnv.unprotectedSecretsDir} (FASTAGENT_SECRETS_DIR) now holds a secret and has ` +
|
|
113
|
+
`no .gitignore — that directory is yours, so fastagent will not write one into it. Make sure git ` +
|
|
114
|
+
`does not track what is in there.`);
|
|
115
|
+
}
|
|
116
|
+
if (dotEnv.written.length > 0) {
|
|
117
|
+
console.error(`[fastagent] wrote ${dotEnv.written.join(", ")} to ${envLabel}`);
|
|
114
118
|
}
|
|
115
|
-
const install = detectRuntime(
|
|
116
|
-
// The kit's manifest lives in channelHome (agentDir when set) — point the install there, not the run root.
|
|
117
|
-
const installCmd = channelHome === target ? install : `(cd ${relative(target, channelHome)} && ${install})`;
|
|
119
|
+
const install = detectRuntime(target, await readPackageJson(target)).runtime === "bun" ? "bun install" : "npm install";
|
|
118
120
|
console.error(` next steps:`);
|
|
119
|
-
console.error(` ${
|
|
121
|
+
console.error(` ${agentFromCwd === undefined ? install : `(cd ${agentFromCwd} && ${install})`} # if @fastagent-sh/fastagent is not installed yet`);
|
|
120
122
|
for (const e of env) {
|
|
121
|
-
if (dotEnv
|
|
123
|
+
if (dotEnv.alreadySet.includes(e.name))
|
|
122
124
|
continue; // the user already has it — nothing to do
|
|
123
|
-
if (dotEnv
|
|
125
|
+
if (dotEnv.written.includes(e.name)) {
|
|
124
126
|
// Written, but its hint may still carry an action (github: paste the same value into the webhook
|
|
125
127
|
// UI) — keep the variable visible instead of silently absorbing it.
|
|
126
|
-
console.error(` ${e.name} — ${e.generate ? "generated and " : ""}written to
|
|
128
|
+
console.error(` ${e.name} — ${e.generate ? "generated and " : ""}written to ${envLabel} # ${e.hint}`);
|
|
127
129
|
continue;
|
|
128
130
|
}
|
|
129
131
|
const value = e.generate ? `=${generated[e.name]}` : "";
|
|
130
132
|
const action = e.required ? "set" : "optionally set";
|
|
131
|
-
console.error(` ${action} ${e.name}${value} in
|
|
133
|
+
console.error(` ${action} ${e.name}${value} in ${envLabel} # ${e.hint}`);
|
|
132
134
|
}
|
|
133
135
|
// Steps carry `{channel}`/`{tools}` path placeholders (their filenames are the scaffold's private
|
|
134
|
-
// knowledge) — resolve them to the real
|
|
135
|
-
const kitPrefix = channelHome === target ? "" : `${relative(target, channelHome)}/`;
|
|
136
|
+
// knowledge) — resolve them to the real agent-dir-relative locations here.
|
|
136
137
|
for (const s of steps) {
|
|
137
|
-
console.error(` ${s.replace("{channel}", relative(target, file)).replace("{tools}",
|
|
138
|
+
console.error(` ${s.replace("{channel}", inAgent(relative(target, file))).replace("{tools}", inAgent("tools"))}`);
|
|
138
139
|
}
|
|
139
140
|
if (ingress === "websocket") {
|
|
140
141
|
console.error(` fastagent dev # no public URL or tunnel required`);
|
|
@@ -221,8 +222,8 @@ async function resolveGroupBehavior(kind, raw) {
|
|
|
221
222
|
value: "context",
|
|
222
223
|
label: "Context-aware groups (recommended)",
|
|
223
224
|
hint: kind === "slack"
|
|
224
|
-
? "bare
|
|
225
|
-
: "bare
|
|
225
|
+
? "bare replies in the Agent's threads + buffer; requires channel/group/mpim history scopes"
|
|
226
|
+
: "bare replies in the Agent's threads + buffer; im:message.group_msg delivers all group messages",
|
|
226
227
|
},
|
|
227
228
|
{
|
|
228
229
|
value: "mentions",
|
|
@@ -241,7 +242,7 @@ async function resolveGroupBehavior(kind, raw) {
|
|
|
241
242
|
}
|
|
242
243
|
/** `fastagent add skill <source> [dir]`: vendor an Agent Skills skill into <dir>/skills/<name>/. */
|
|
243
244
|
export async function runAddSkill(source, dirArg, opts) {
|
|
244
|
-
const target = resolve(dirArg);
|
|
245
|
+
const { agentDir: target } = placementOrExit(resolve(dirArg));
|
|
245
246
|
if (!source) {
|
|
246
247
|
// A missing source is a usage error (exit 2), but the guide is worth more than a bare
|
|
247
248
|
// missing-argument line — the common path (writing your own skill) needs no command at all.
|
|
@@ -254,10 +255,8 @@ export async function runAddSkill(source, dirArg, opts) {
|
|
|
254
255
|
` bare name found in your global skill dirs (~/.agents/skills, ~/.pi/agent/skills)\n` +
|
|
255
256
|
` --update overwrites an existing skill (re-fetch from source); review with git diff`);
|
|
256
257
|
}
|
|
257
|
-
// Skills are agent surface — vendored into
|
|
258
|
-
const {
|
|
259
|
-
const skillHome = resolveAgentDir(target, skillConfig);
|
|
260
|
-
const { name, description, dest, hasScripts, diagnostics, overwritten } = await vendorSkill(skillHome, source, {
|
|
258
|
+
// Skills are agent surface — vendored into the agent dir's `skills/`.
|
|
259
|
+
const { name, description, dest, hasScripts, diagnostics, overwritten } = await vendorSkill(target, source, {
|
|
261
260
|
update: opts.update ?? false,
|
|
262
261
|
}).catch(failStartup);
|
|
263
262
|
console.error(`[fastagent] ${overwritten ? "updated" : "vendored"} skill "${name}" → ${dest}/`);
|
|
@@ -13,12 +13,12 @@ import { readFileSync } from "node:fs";
|
|
|
13
13
|
import { join, resolve } from "node:path";
|
|
14
14
|
import { createInterface } from "node:readline";
|
|
15
15
|
import { loadDotEnv } from "../../env.js";
|
|
16
|
-
import { resolveStateRoot } from "../../
|
|
16
|
+
import { resolveStateRoot } from "../../paths.js";
|
|
17
17
|
import { log, setLogLevel } from "../../log.js";
|
|
18
18
|
import { ABORTED_CODE, SESSION_BUSY_CODE } from "../../agent.js";
|
|
19
19
|
import { NO_ACTIVE_RUN_CODE } from "../../session.js";
|
|
20
20
|
import { ControlRequestError, connectAgent, connectSessionControl } from "../../session-remote.js";
|
|
21
|
-
import { failStartup } from "../fail.js";
|
|
21
|
+
import { failStartup, placementOrExit } from "../fail.js";
|
|
22
22
|
/** Read the serving process's local discovery file. */
|
|
23
23
|
function discover(dir) {
|
|
24
24
|
const path = join(resolveStateRoot(dir), "control.json");
|
|
@@ -107,8 +107,6 @@ async function drainEvents(iterator, io) {
|
|
|
107
107
|
}
|
|
108
108
|
export async function runAttach(sessionArg, dirArg, opts) {
|
|
109
109
|
setLogLevel("info");
|
|
110
|
-
const dir = resolve(dirArg ?? ".");
|
|
111
|
-
loadDotEnv(dir);
|
|
112
110
|
// --url and --token travel together, and BOTH must be non-empty: a lone --url (or an empty
|
|
113
111
|
// token) silently falling back to the LOCAL control.json would attach (and steer!) a same-named
|
|
114
112
|
// local session while the user believes they are remote. One predicate decides — the guard and
|
|
@@ -117,6 +115,12 @@ export async function runAttach(sessionArg, dirArg, opts) {
|
|
|
117
115
|
if (remote && !(opts.url && opts.token)) {
|
|
118
116
|
failStartup(new Error("--url and --token must be given together and non-empty"));
|
|
119
117
|
}
|
|
118
|
+
// A REMOTE attach reads nothing under `dir` — no control.json to discover, no agent to assemble —
|
|
119
|
+
// so requiring one would refuse the command's whole point (a laptop attaching to a deployed box).
|
|
120
|
+
// Placement is resolved only for the local-discovery path, which genuinely needs an agent.
|
|
121
|
+
const dir = remote ? resolve(dirArg ?? ".") : placementOrExit(resolve(dirArg ?? ".")).agentDir;
|
|
122
|
+
if (!remote)
|
|
123
|
+
loadDotEnv(dir);
|
|
120
124
|
// For a discovered endpoint the FIRST read joins the startup budget below: the dev-watch
|
|
121
125
|
// restart window has two halves — control.json unlinked (not yet rewritten) and port not yet
|
|
122
126
|
// bound — and dying instantly on the first half would contradict the grace the second half gets.
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { loadDotEnv } from "../../env.js";
|
|
4
4
|
import { installProxyFetch } from "../../proxy.js";
|
|
5
|
-
import { failStartup } from "../fail.js";
|
|
5
|
+
import { failStartup, placementOrExit } from "../fail.js";
|
|
6
6
|
import { resolveFirstRunModel } from "../shared.js";
|
|
7
7
|
export async function runChat(dirArg, opts) {
|
|
8
|
-
const
|
|
9
|
-
loadDotEnv(
|
|
8
|
+
const placement = placementOrExit(resolve(dirArg));
|
|
9
|
+
loadDotEnv(placement.agentDir);
|
|
10
10
|
installProxyFetch(); // model calls (and the login dialog) must go through the proxy too
|
|
11
11
|
// First-run funnel, FULL picker: chat authenticates through fastagent's credential store like every
|
|
12
12
|
// other command (the shared session builder injects it — see engines/pi/session-builder.ts), so the
|
|
13
13
|
// credential-annotated catalog and inline login apply here too.
|
|
14
|
-
await resolveFirstRunModel(
|
|
15
|
-
// Run the chat process
|
|
16
|
-
// so aligning process.cwd() with the workspace keeps a cwd-less session on
|
|
17
|
-
process.chdir(
|
|
14
|
+
await resolveFirstRunModel(placement.agentDir, { model: opts.model, authPath: opts.authPath });
|
|
15
|
+
// Run the chat process AT the workspace: pi resolves a session's cwd as `header.cwd ?? process.cwd()`,
|
|
16
|
+
// so aligning process.cwd() with the workspace keeps a cwd-less session on it. Paths are absolute.
|
|
17
|
+
process.chdir(placement.workspace);
|
|
18
18
|
// Lazy-import: chat pulls pi's interactive TUI module graph; headless start/dev never need it.
|
|
19
19
|
const { runPiChat } = await import("../../engines/pi/chat.js");
|
|
20
|
-
await runPiChat(
|
|
20
|
+
await runPiChat(placement.workspace, { model: opts.model, authPath: opts.authPath }).catch(failStartup);
|
|
21
21
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DeployHost = "docker" | "fly" | "railway";
|
|
1
|
+
export type DeployHost = "docker" | "fly" | "railway" | "agentcore";
|
|
2
2
|
export interface DeployOptions {
|
|
3
3
|
run?: boolean;
|
|
4
4
|
tunnel?: boolean;
|
|
@@ -13,3 +13,16 @@ export interface DeployOptions {
|
|
|
13
13
|
input?: boolean;
|
|
14
14
|
}
|
|
15
15
|
export declare function runDeploy(host: DeployHost, dirArg: string, opts: DeployOptions): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Write the plan's artifacts under `target`, honouring the ownership rule: a file we did not generate is
|
|
18
|
+
* never touched, ours is kept unless `--force`. Exported for its own test — this is a four-branch state
|
|
19
|
+
* machine over (exists, ours, force) that used to be proven by spawning the CLI eight times, which is
|
|
20
|
+
* command LOGIC re-run through a subprocess (see vitest.config.ts) and the suite's slowest test.
|
|
21
|
+
*/
|
|
22
|
+
export declare function writeArtifacts(target: string, artifacts: {
|
|
23
|
+
path: string;
|
|
24
|
+
content: string;
|
|
25
|
+
}[], options: {
|
|
26
|
+
force: boolean;
|
|
27
|
+
alwaysWrite?: string[];
|
|
28
|
+
}): Promise<void>;
|