@fastagent-sh/fastagent 0.21.0 → 0.22.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 +9 -12
- package/dist/agent.d.ts +24 -37
- package/dist/agent.js +7 -15
- package/dist/atomic-write.d.ts +13 -17
- package/dist/atomic-write.js +18 -29
- package/dist/bind.d.ts +12 -22
- package/dist/bind.js +18 -27
- package/dist/channel.d.ts +11 -33
- package/dist/channels/agentcore-limits.d.ts +2 -13
- package/dist/channels/agentcore-limits.js +2 -13
- package/dist/channels/agentcore-protocol.d.ts +67 -58
- package/dist/channels/agentcore-protocol.js +23 -15
- package/dist/channels/agentcore-service.d.ts +19 -33
- package/dist/channels/agentcore-service.js +176 -59
- package/dist/channels/agentcore.d.ts +39 -33
- package/dist/channels/agentcore.js +90 -224
- package/dist/channels/body.d.ts +15 -2
- package/dist/channels/body.js +24 -2
- package/dist/channels/busy.d.ts +1 -21
- package/dist/channels/busy.js +2 -39
- package/dist/channels/control.d.ts +17 -62
- package/dist/channels/control.js +74 -182
- package/dist/channels/define-channel.d.ts +39 -0
- package/dist/channels/define-channel.js +9 -0
- package/dist/channels/discover.d.ts +10 -31
- package/dist/channels/discover.js +37 -31
- package/dist/channels/feishu/bootstrap-token.d.ts +1 -8
- package/dist/channels/feishu/bootstrap-token.js +7 -23
- package/dist/channels/feishu/card.d.ts +14 -28
- package/dist/channels/feishu/card.js +16 -30
- package/dist/channels/feishu/cloud.d.ts +1 -5
- package/dist/channels/feishu/context-buffer.d.ts +4 -23
- package/dist/channels/feishu/context-buffer.js +8 -31
- package/dist/channels/feishu/crypto.d.ts +10 -5
- package/dist/channels/feishu/crypto.js +12 -19
- package/dist/channels/feishu/feishu-api.d.ts +2 -1
- package/dist/channels/feishu/feishu-api.js +5 -4
- package/dist/channels/feishu/feishu.d.ts +4 -10
- package/dist/channels/feishu/feishu.js +113 -227
- package/dist/channels/feishu/invoke-turn.d.ts +5 -4
- package/dist/channels/feishu/invoke-turn.js +36 -54
- package/dist/channels/feishu/model.d.ts +4 -21
- package/dist/channels/feishu/model.js +1 -8
- package/dist/channels/feishu/normalize.d.ts +2 -10
- package/dist/channels/feishu/normalize.js +7 -42
- package/dist/channels/feishu/parse.d.ts +9 -29
- package/dist/channels/feishu/parse.js +8 -26
- package/dist/channels/feishu/preview.d.ts +12 -14
- package/dist/channels/feishu/preview.js +108 -207
- package/dist/channels/feishu/register-app.d.ts +8 -23
- package/dist/channels/feishu/register-app.js +7 -20
- package/dist/channels/feishu/register-webhook.d.ts +2 -9
- package/dist/channels/feishu/register-webhook.js +9 -33
- package/dist/channels/feishu/scaffold/channel.ts +27 -19
- package/dist/channels/feishu/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/feishu/setup-mode.d.ts +11 -36
- package/dist/channels/feishu/setup-mode.js +7 -26
- package/dist/channels/feishu/ws-ingress.d.ts +1 -2
- package/dist/channels/feishu/ws-ingress.js +6 -8
- package/dist/channels/http.d.ts +15 -15
- package/dist/channels/http.js +19 -9
- package/dist/channels/kit/attachment-path.d.ts +4 -5
- package/dist/channels/kit/attachment-path.js +10 -34
- package/dist/channels/kit/context-buffer.d.ts +14 -21
- package/dist/channels/kit/context-buffer.js +25 -38
- package/dist/channels/kit/delivery.d.ts +35 -0
- package/dist/channels/kit/delivery.js +129 -0
- package/dist/channels/kit/event-stream.d.ts +5 -0
- package/dist/channels/kit/event-stream.js +29 -0
- package/dist/channels/kit/invoke-turn-kit.d.ts +53 -26
- package/dist/channels/kit/invoke-turn-kit.js +89 -32
- package/dist/channels/kit/preview-kit.d.ts +18 -72
- package/dist/channels/kit/preview-kit.js +22 -88
- package/dist/channels/kit/signature.d.ts +3 -9
- package/dist/channels/kit/signature.js +3 -9
- package/dist/channels/kit/state.d.ts +1 -2
- package/dist/channels/kit/state.js +5 -14
- package/dist/channels/kit/stop-command.d.ts +1 -2
- package/dist/channels/kit/stop-command.js +6 -9
- package/dist/channels/kit/tasks.d.ts +1 -1
- package/dist/channels/kit/tasks.js +8 -21
- package/dist/channels/kit/text.d.ts +2 -16
- package/dist/channels/kit/text.js +23 -31
- package/dist/channels/kit/thread-participants.d.ts +4 -17
- package/dist/channels/kit/thread-participants.js +8 -55
- package/dist/channels/kit/transport.d.ts +26 -0
- package/dist/channels/kit/transport.js +20 -0
- package/dist/channels/kit/turn-queue.d.ts +7 -11
- package/dist/channels/kit/turn-queue.js +21 -39
- package/dist/channels/kit/turn-runner.d.ts +32 -18
- package/dist/channels/kit/turn-runner.js +98 -53
- package/dist/channels/kit/turn-store.d.ts +32 -32
- package/dist/channels/kit/turn-store.js +45 -88
- package/dist/channels/lark/lark.d.ts +1 -5
- package/dist/channels/lark/onboard.d.ts +3 -12
- package/dist/channels/lark/onboard.js +3 -13
- package/dist/channels/lark/scaffold/channel.ts +27 -19
- package/dist/channels/lark/scaffold/channel.websocket.ts +31 -0
- package/dist/channels/registration.d.ts +9 -35
- package/dist/channels/registration.js +6 -37
- package/dist/channels/secret.js +2 -5
- package/dist/channels/serve.d.ts +47 -44
- package/dist/channels/serve.js +165 -101
- package/dist/channels/slack/config-api.d.ts +4 -27
- package/dist/channels/slack/config-api.js +5 -34
- package/dist/channels/slack/context-buffer.d.ts +2 -3
- package/dist/channels/slack/context-buffer.js +5 -5
- package/dist/channels/slack/invoke-turn.d.ts +3 -1
- package/dist/channels/slack/invoke-turn.js +25 -34
- package/dist/channels/slack/manifest.js +1 -3
- package/dist/channels/slack/model.d.ts +1 -2
- package/dist/channels/slack/model.js +1 -1
- package/dist/channels/slack/onboard.d.ts +4 -3
- package/dist/channels/slack/onboard.js +8 -11
- package/dist/channels/slack/onboarding-state.d.ts +3 -5
- package/dist/channels/slack/onboarding-state.js +9 -6
- package/dist/channels/slack/parse.d.ts +4 -13
- package/dist/channels/slack/parse.js +7 -28
- package/dist/channels/slack/preview.d.ts +12 -2
- package/dist/channels/slack/preview.js +292 -269
- package/dist/channels/slack/reaction.d.ts +11 -3
- package/dist/channels/slack/reaction.js +19 -8
- package/dist/channels/slack/register-webhook.js +4 -7
- package/dist/channels/slack/scaffold/channel.ts +20 -13
- package/dist/channels/slack/scaffold/slack-send.ts +2 -2
- package/dist/channels/slack/setup-server.d.ts +1 -5
- package/dist/channels/slack/setup-server.js +1 -5
- package/dist/channels/slack/shared-api.d.ts +1 -4
- package/dist/channels/slack/shared-api.js +2 -13
- package/dist/channels/slack/slack-api.d.ts +2 -1
- package/dist/channels/slack/slack-api.js +6 -4
- package/dist/channels/slack/slack.d.ts +11 -14
- package/dist/channels/slack/slack.js +121 -122
- package/dist/channels/sse.d.ts +1 -1
- package/dist/channels/sse.js +1 -1
- package/dist/channels/telegram/context-buffer.d.ts +7 -22
- package/dist/channels/telegram/context-buffer.js +6 -23
- package/dist/channels/telegram/invoke-turn.d.ts +7 -7
- package/dist/channels/telegram/invoke-turn.js +24 -43
- package/dist/channels/telegram/parse.d.ts +24 -51
- package/dist/channels/telegram/parse.js +32 -84
- package/dist/channels/telegram/preview.d.ts +10 -1
- package/dist/channels/telegram/preview.js +79 -100
- package/dist/channels/telegram/register-webhook.js +5 -23
- package/dist/channels/telegram/scaffold/channel.ts +25 -18
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -4
- package/dist/channels/telegram/telegram-api.d.ts +4 -3
- package/dist/channels/telegram/telegram-api.js +9 -7
- package/dist/channels/telegram/telegram.d.ts +4 -18
- package/dist/channels/telegram/telegram.js +38 -94
- package/dist/channels/telegram/turn-store.d.ts +11 -14
- package/dist/channels/telegram/turn-store.js +8 -6
- package/dist/channels/wait-health.d.ts +4 -6
- package/dist/channels/wait-health.js +10 -14
- package/dist/cli/add-feishu.d.ts +2 -12
- package/dist/cli/add-feishu.js +24 -71
- package/dist/cli/add-slack.d.ts +5 -3
- package/dist/cli/add-slack.js +53 -76
- package/dist/cli/auth-view.d.ts +2 -16
- package/dist/cli/auth-view.js +2 -16
- package/dist/cli/commands/add.js +25 -43
- package/dist/cli/commands/chat.d.ts +0 -1
- package/dist/cli/commands/chat.js +5 -6
- package/dist/cli/commands/deploy/agentcore.js +60 -65
- package/dist/cli/commands/deploy/docker.js +50 -23
- package/dist/cli/commands/deploy/fly.js +23 -61
- package/dist/cli/commands/deploy/railway.js +23 -18
- package/dist/cli/commands/deploy/shared.d.ts +38 -57
- package/dist/cli/commands/deploy/shared.js +40 -59
- package/dist/cli/commands/deploy.d.ts +10 -27
- package/dist/cli/commands/deploy.js +38 -60
- package/dist/cli/commands/destroy.d.ts +4 -0
- package/dist/cli/commands/destroy.js +56 -0
- package/dist/cli/commands/dev.d.ts +2 -1
- package/dist/cli/commands/dev.js +20 -23
- package/dist/cli/commands/info.d.ts +0 -2
- package/dist/cli/commands/info.js +83 -49
- package/dist/cli/commands/init.d.ts +1 -2
- package/dist/cli/commands/init.js +16 -55
- package/dist/cli/commands/invoke.d.ts +0 -1
- package/dist/cli/commands/invoke.js +5 -7
- package/dist/cli/commands/login.d.ts +2 -1
- package/dist/cli/commands/login.js +33 -38
- package/dist/cli/commands/logs.js +3 -4
- package/dist/cli/commands/models.js +1 -5
- package/dist/cli/commands/routine-run.d.ts +6 -0
- package/dist/cli/commands/routine-run.js +47 -0
- package/dist/cli/commands/routine.d.ts +25 -0
- package/dist/cli/commands/routine.js +122 -0
- package/dist/cli/commands/start.d.ts +31 -3
- package/dist/cli/commands/start.js +131 -81
- package/dist/cli/commands/tool.js +26 -12
- package/dist/cli/fail.d.ts +13 -23
- package/dist/cli/fail.js +20 -23
- package/dist/cli/invoke-stream.d.ts +1 -8
- package/dist/cli/kernel.d.ts +6 -38
- package/dist/cli/kernel.js +14 -77
- package/dist/cli/models-view.d.ts +1 -9
- package/dist/cli/models-view.js +11 -22
- package/dist/cli/program.d.ts +5 -6
- package/dist/cli/program.js +120 -157
- package/dist/cli/serve.d.ts +57 -41
- package/dist/cli/serve.js +94 -121
- package/dist/cli/shared.d.ts +15 -61
- package/dist/cli/shared.js +55 -181
- package/dist/cli.js +1 -8
- package/dist/collect.d.ts +6 -0
- package/dist/collect.js +6 -0
- package/dist/core.d.ts +4 -1
- package/dist/core.js +9 -7
- package/dist/declared-secrets.d.ts +55 -0
- package/dist/declared-secrets.js +88 -0
- package/dist/deploy/agentcore/aws-cli.d.ts +91 -0
- package/dist/deploy/agentcore/aws-cli.js +64 -0
- package/dist/deploy/agentcore/destroy.d.ts +51 -0
- package/dist/deploy/agentcore/destroy.js +273 -0
- package/dist/deploy/agentcore/forwarder.js +28 -79
- package/dist/deploy/agentcore/logs.d.ts +2 -18
- package/dist/deploy/agentcore/logs.js +27 -51
- package/dist/deploy/agentcore/plan.d.ts +85 -105
- package/dist/deploy/agentcore/plan.js +152 -314
- package/dist/deploy/agentcore/run.d.ts +28 -28
- package/dist/deploy/agentcore/run.js +227 -306
- package/dist/deploy/agentcore/zip.d.ts +2 -15
- package/dist/deploy/agentcore/zip.js +3 -17
- package/dist/deploy/build-context.d.ts +21 -0
- package/dist/deploy/build-context.js +137 -0
- package/dist/deploy/channel-ingress.d.ts +26 -39
- package/dist/deploy/channel-ingress.js +57 -28
- package/dist/deploy/container.d.ts +20 -34
- package/dist/deploy/container.js +49 -100
- package/dist/deploy/docker/plan.d.ts +12 -11
- package/dist/deploy/docker/plan.js +43 -24
- package/dist/deploy/docker/run.d.ts +22 -25
- package/dist/deploy/docker/run.js +39 -37
- package/dist/deploy/fly/plan.d.ts +15 -44
- package/dist/deploy/fly/plan.js +41 -73
- package/dist/deploy/fly/run.d.ts +16 -51
- package/dist/deploy/fly/run.js +42 -95
- package/dist/deploy/hosts.d.ts +24 -3
- package/dist/deploy/hosts.js +24 -3
- package/dist/deploy/preflight.d.ts +47 -41
- package/dist/deploy/preflight.js +282 -383
- package/dist/deploy/railway/plan.d.ts +16 -42
- package/dist/deploy/railway/plan.js +42 -60
- package/dist/deploy/railway/run.d.ts +30 -68
- package/dist/deploy/railway/run.js +86 -100
- package/dist/deploy/registration-gate.d.ts +6 -13
- package/dist/deploy/residency.d.ts +37 -0
- package/dist/deploy/residency.js +24 -0
- package/dist/deploy/runner.d.ts +10 -9
- package/dist/deploy/runner.js +7 -18
- package/dist/deploy/secrets.d.ts +40 -41
- package/dist/deploy/secrets.js +117 -103
- package/dist/deploy/workspace.d.ts +50 -0
- package/dist/deploy/workspace.js +197 -0
- package/dist/dev-supervisor.d.ts +1 -9
- package/dist/dev-supervisor.js +21 -64
- package/dist/effect-port.d.ts +50 -0
- package/dist/effect-port.js +77 -0
- package/dist/engines/pi/agent-session-factory.d.ts +50 -69
- package/dist/engines/pi/agent-session-factory.js +249 -146
- package/dist/engines/pi/auth.d.ts +15 -11
- package/dist/engines/pi/auth.js +96 -99
- package/dist/engines/pi/chat.d.ts +1 -5
- package/dist/engines/pi/chat.js +2 -14
- package/dist/engines/pi/config.d.ts +53 -51
- package/dist/engines/pi/config.js +83 -92
- package/dist/engines/pi/create.d.ts +60 -75
- package/dist/engines/pi/create.js +108 -141
- package/dist/engines/pi/definition.d.ts +14 -58
- package/dist/engines/pi/definition.js +21 -85
- package/dist/engines/pi/invoke-session.d.ts +5 -25
- package/dist/engines/pi/invoke-session.js +209 -244
- package/dist/engines/pi/login.d.ts +95 -35
- package/dist/engines/pi/login.js +259 -107
- package/dist/engines/pi/machine.d.ts +31 -0
- package/dist/engines/pi/machine.js +106 -0
- package/dist/engines/pi/models.d.ts +70 -89
- package/dist/engines/pi/models.js +188 -81
- package/dist/engines/pi/open.d.ts +65 -53
- package/dist/engines/pi/open.js +129 -69
- package/dist/engines/pi/report.d.ts +3 -12
- package/dist/engines/pi/report.js +2 -16
- package/dist/engines/pi/search-tools.d.ts +1 -9
- package/dist/engines/pi/search-tools.js +26 -57
- package/dist/engines/pi/service.d.ts +2 -16
- package/dist/engines/pi/service.js +2 -16
- package/dist/engines/pi/session-builder.d.ts +5 -9
- package/dist/engines/pi/session-builder.js +44 -82
- package/dist/engines/pi/session-control.js +286 -269
- package/dist/engines/pi/session-effects.d.ts +14 -0
- package/dist/engines/pi/session-effects.js +30 -0
- package/dist/engines/pi/session-inheritance.d.ts +9 -18
- package/dist/engines/pi/session-inheritance.js +103 -160
- package/dist/engines/pi/session-markers.d.ts +31 -30
- package/dist/engines/pi/session-markers.js +26 -25
- package/dist/engines/pi/session-settings.d.ts +6 -22
- package/dist/engines/pi/session-settings.js +10 -21
- package/dist/engines/pi/session-store.d.ts +26 -92
- package/dist/engines/pi/session-store.js +96 -310
- package/dist/engines/pi/tool-context.d.ts +17 -45
- package/dist/engines/pi/tool-context.js +7 -26
- package/dist/engines/pi/tool.d.ts +45 -36
- package/dist/engines/pi/tool.js +43 -57
- package/dist/engines/pi/turn-kit.d.ts +14 -52
- package/dist/engines/pi/turn-kit.js +18 -67
- package/dist/engines/pi/wake-tool.d.ts +2 -10
- package/dist/engines/pi/wake-tool.js +7 -27
- package/dist/env.d.ts +27 -28
- package/dist/env.js +45 -50
- package/dist/index.js +0 -2
- package/dist/lark.d.ts +1 -2
- package/dist/lark.js +1 -2
- package/dist/loader.d.ts +14 -55
- package/dist/loader.js +24 -43
- package/dist/log.d.ts +2 -12
- package/dist/log.js +3 -24
- package/dist/node.d.ts +2 -15
- package/dist/node.js +2 -18
- package/dist/observe.d.ts +2 -8
- package/dist/open-url.d.ts +1 -1
- package/dist/open-url.js +1 -1
- package/dist/paths.d.ts +70 -134
- package/dist/paths.js +165 -232
- package/dist/pi.d.ts +4 -6
- package/dist/pi.js +3 -3
- package/dist/proxy.d.ts +16 -6
- package/dist/proxy.js +52 -16
- package/dist/runtime.d.ts +18 -12
- package/dist/runtime.js +18 -11
- package/dist/scaffold/add-channel.d.ts +14 -31
- package/dist/scaffold/add-channel.js +48 -112
- package/dist/scaffold/init.d.ts +9 -31
- package/dist/scaffold/init.js +45 -138
- package/dist/scaffold/templates/env.example +6 -4
- package/dist/scaffold/templates/fastagent.config.ts +36 -0
- package/dist/scaffold/templates.d.ts +3 -3
- package/dist/scaffold/templates.js +5 -8
- package/dist/scaffold/vendor-skill.d.ts +2 -5
- package/dist/scaffold/vendor-skill.js +19 -29
- package/dist/schedule/cron.d.ts +9 -5
- package/dist/schedule/cron.js +9 -11
- package/dist/schedule/discover.d.ts +12 -12
- package/dist/schedule/discover.js +43 -34
- package/dist/schedule/routine.d.ts +43 -0
- package/dist/schedule/routine.js +32 -0
- package/dist/schedule/run.d.ts +55 -0
- package/dist/schedule/run.js +146 -0
- package/dist/schedule/scheduler.d.ts +44 -55
- package/dist/schedule/scheduler.js +291 -265
- package/dist/schedule/state.d.ts +105 -10
- package/dist/schedule/state.js +293 -19
- package/dist/schedule/wake-alarm.d.ts +10 -23
- package/dist/schedule/wake-alarm.js +91 -121
- package/dist/schedule/wakeups.d.ts +23 -39
- package/dist/schedule/wakeups.js +33 -75
- package/dist/secrets-gate.d.ts +32 -0
- package/dist/secrets-gate.js +33 -0
- package/dist/service.d.ts +137 -98
- package/dist/service.js +250 -252
- package/dist/session-remote.d.ts +33 -44
- package/dist/session-remote.js +203 -192
- package/dist/session.d.ts +153 -228
- package/dist/session.js +33 -55
- package/dist/tunnel.d.ts +8 -36
- package/dist/tunnel.js +24 -65
- package/dist/version.d.ts +2 -2
- package/dist/version.js +2 -2
- package/package.json +10 -16
- package/dist/channels/agentcore-state.d.ts +0 -77
- package/dist/channels/agentcore-state.js +0 -276
- package/dist/channels/github/github.d.ts +0 -31
- package/dist/channels/github/github.js +0 -89
- package/dist/channels/github/scaffold/channel.ts +0 -25
- package/dist/cli/commands/attach.d.ts +0 -108
- package/dist/cli/commands/attach.js +0 -637
- package/dist/cli/commands/fire.d.ts +0 -7
- package/dist/cli/commands/fire.js +0 -38
- package/dist/cli/commands/schedule.d.ts +0 -12
- package/dist/cli/commands/schedule.js +0 -87
- package/dist/github.d.ts +0 -3
- package/dist/github.js +0 -2
- package/dist/scaffold/templates/fastagent.config.mjs +0 -23
- package/dist/schedule/audit.d.ts +0 -18
- package/dist/schedule/audit.js +0 -57
- package/dist/schedule/schedule.d.ts +0 -35
- package/dist/schedule/schedule.js +0 -23
package/dist/scaffold/init.js
CHANGED
|
@@ -1,71 +1,32 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Init: scaffold a runnable fastagent agent, offline. Default = a COMPLETE agent (persona.md +
|
|
3
|
-
* the writing-great-skills skill + a fetch-url code tool + fastagent.config.mjs + package.json +
|
|
4
|
-
* .gitignore + .secrets/); `--minimal` drops the code tool and package.json. persona.md is the agent's
|
|
5
|
-
* identity (prompt segment ①); an existing AGENTS.md is never written or touched — it is project
|
|
6
|
-
* context (②), kept as-is. skills/ and tools/ are the agent's self-editable capabilities (re-read each
|
|
7
|
-
* turn).
|
|
8
|
-
*
|
|
9
|
-
* Placement — no detection and no prompt, just a default and one flag. By DEFAULT the whole agent —
|
|
10
|
-
* definition, config, `.secrets/`, machinery — lands in `<dir>/fastagent/`; the surrounding tree gets
|
|
11
|
-
* ZERO writes and becomes the workspace the agent works on. `--agent-dir <name>` picks another name for
|
|
12
|
-
* that directory (`fastagent.config.*` is the marker of what IS an agent, never the name), and `--agent-dir .`
|
|
13
|
-
* (spelled `--flat`) lands the identical shape in `dir` itself, for the case where the directory IS the
|
|
14
|
-
* agent (a standalone agent repo, a monorepo package).
|
|
15
|
-
*
|
|
16
|
-
* Which of the two a served agent turns out to be is NOT decided here: the workspace is whatever
|
|
17
|
-
* fastagent is later pointed at (resolvePlacement). This module only chooses where the files land — so
|
|
18
|
-
* its one placement duty is to refuse a target that the lookup would not return (see below).
|
|
19
|
-
*
|
|
20
|
-
* Scope: init is best-effort atomic for ORDINARY inputs — it never overwrites existing files,
|
|
21
|
-
* preflights non-directory scaffold parents, and rolls back a partial write (files AND the
|
|
22
|
-
* `fastagent/` tree it created for them, so a retry sees a clean slate). It does not defend against
|
|
23
|
-
* every pathological target state (TOCTOU, FIFOs, disk-full): recover by delete-and-retry.
|
|
24
|
-
*
|
|
25
|
-
* Sibling scaffold modules: add-channel.ts (`add <channel>`), vendor-skill.ts (`add skill`). The files
|
|
26
|
-
* this module writes are real templates under templates/, read through templates.ts.
|
|
27
|
-
*/
|
|
1
|
+
/** Init: scaffold a runnable fastagent agent, offline. */
|
|
28
2
|
import { lstat, mkdir, readdir, rm, rmdir, writeFile } from "node:fs/promises";
|
|
29
3
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
30
|
-
import {
|
|
4
|
+
import { AGENT_CONFIG_FILE, DEFAULT_AGENT_DIRNAME, SECRETS_DIRNAME, agentDefinitionOwner, agentsAt, displayPath, exists, } from "../paths.js";
|
|
31
5
|
import { baseTemplate, packageJson, toPackageName } from "./templates.js";
|
|
32
6
|
import { fastagentVersion } from "../version.js";
|
|
33
|
-
/** The agent directory name for a raw `--agent-dir` value: the default when unset, and `./bot` read as
|
|
34
|
-
* `bot` — `basename` already says that is what it means, so rejecting the spelling would be pedantry. */
|
|
7
|
+
/** The agent directory name for a raw `--agent-dir` value: the default when unset, and `./bot` read as `bot`. */
|
|
35
8
|
export function agentDirName(raw) {
|
|
36
9
|
if (raw === undefined)
|
|
37
10
|
return DEFAULT_AGENT_DIRNAME;
|
|
38
11
|
const trimmed = raw.replace(/^\.[/\\]/, "");
|
|
39
12
|
return trimmed === "" ? raw : trimmed;
|
|
40
13
|
}
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
* one-level lookup cannot see it — an agent nothing would ever serve.
|
|
14
|
+
/**
|
|
15
|
+
* Why `name` cannot be an agent directory name, or undefined when it can.
|
|
44
16
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
17
|
+
* `"."` is refused along with every path: the definition always lands in a SUBDIRECTORY, and the directory around it
|
|
18
|
+
* is the workspace. The subdirectory also carries its own `package.json`, so a `fastagent.config.ts` never loads under
|
|
19
|
+
* the host repo's module settings.
|
|
20
|
+
*/
|
|
48
21
|
export function agentDirNameError(name) {
|
|
49
|
-
if (name
|
|
22
|
+
if (name !== "" && name !== "." && name !== ".." && name === basename(name))
|
|
50
23
|
return undefined;
|
|
51
|
-
return (`must be a single directory name
|
|
52
|
-
`the
|
|
24
|
+
return (`must be a single directory name — the definition lives in a subdirectory, and the directory around it is ` +
|
|
25
|
+
`the workspace the agent works on (a path, or ".", would put it elsewhere)`);
|
|
53
26
|
}
|
|
54
|
-
/**
|
|
55
|
-
* Scaffold a runnable agent into `<dir>/<agentDir>/` — or into `dir` itself when `agentDir` is `"."`
|
|
56
|
-
* (both created if missing). Default is a complete agent (persona.md + the writing-great-skills skill +
|
|
57
|
-
* a code tool + package.json); `--minimal` drops the code tool and package.json.
|
|
58
|
-
*
|
|
59
|
-
* A SUBDIRECTORY target must be empty (any content there is an unfinished agent or something unrelated,
|
|
60
|
-
* and landing persona.md beside it would be a silent mix), while `"."` is a directory being adopted —
|
|
61
|
-
* content is expected. So `"."` KEEPS every file that already exists (reported, never overwritten, never
|
|
62
|
-
* verified) and refuses only on a config, which means the directory is already an agent. An existing
|
|
63
|
-
* AGENTS.md is untouched either way: that is the project's context, adopted as-is.
|
|
64
|
-
*/
|
|
27
|
+
/** Scaffold a runnable agent into `<dir>/<agentDir>/` (both created if missing). */
|
|
65
28
|
export async function scaffoldAgent(dir, options = {}) {
|
|
66
|
-
const minimal = options.minimal ?? false;
|
|
67
29
|
const root = agentDirName(options.agentDir);
|
|
68
|
-
const flat = root === ".";
|
|
69
30
|
const invalid = agentDirNameError(root);
|
|
70
31
|
if (invalid)
|
|
71
32
|
throw new Error(`agentDir "${root}" ${invalid}`);
|
|
@@ -74,45 +35,35 @@ export async function scaffoldAgent(dir, options = {}) {
|
|
|
74
35
|
content: baseTemplate(`skills/writing-great-skills/${name}`),
|
|
75
36
|
});
|
|
76
37
|
const files = [
|
|
77
|
-
// ① identity.
|
|
78
|
-
// an existing repo already owns its AGENTS.md (kept untouched, read as ② context from the workspace).
|
|
38
|
+
// ① identity.
|
|
79
39
|
{ rel: join(root, "persona.md"), content: baseTemplate("persona.md") },
|
|
80
|
-
// The example skill: how to author skills well — the core of self-iteration.
|
|
81
|
-
// ships in --minimal too. Vendored verbatim from mattpocock/skills (MIT); LICENSE sits beside it.
|
|
40
|
+
// The example skill: how to author skills well — the core of self-iteration.
|
|
82
41
|
skill("SKILL.md"),
|
|
83
42
|
skill("GLOSSARY.md"),
|
|
84
43
|
skill("LICENSE"),
|
|
85
|
-
{ rel: join(root,
|
|
86
|
-
// Two ignore files, scaffolded ONCE and owned by the author from then on — no command rewrites,
|
|
87
|
-
//
|
|
88
|
-
// carries its own because the root file is the one the author has reason to edit, and git's
|
|
89
|
-
// nested-ignore precedence keeps the credentials protected whatever happens up there.
|
|
44
|
+
{ rel: join(root, AGENT_CONFIG_FILE), content: baseTemplate(AGENT_CONFIG_FILE) },
|
|
45
|
+
// Two ignore files, scaffolded ONCE and owned by the author from then on — no command rewrites, reads or verifies
|
|
46
|
+
// them.
|
|
90
47
|
{ rel: join(root, ".gitignore"), content: baseTemplate("gitignore") },
|
|
91
48
|
{ rel: join(root, SECRETS_DIRNAME, ".gitignore"), content: baseTemplate("secrets.gitignore") },
|
|
92
49
|
{ rel: join(root, SECRETS_DIRNAME, ".env.example"), content: baseTemplate("env.example") },
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
// The agent's own manifest, named after the directory it serves (`<dir>-agent`) — except when it
|
|
97
|
-
// IS that directory, where it takes the name straight.
|
|
50
|
+
{ rel: join(root, "tools", "fetch-url.ts"), content: baseTemplate("tools/fetch-url.ts") },
|
|
51
|
+
// The agent's own manifest, named after the directory it serves. Its `type: module` is why the definition gets a
|
|
52
|
+
// subdirectory of its own: a config file under a host repo's CommonJS manifest does not load.
|
|
98
53
|
{
|
|
99
54
|
rel: join(root, "package.json"),
|
|
100
|
-
content: packageJson(
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Inside another agent's DEFINITION (its `skills/`, `tools/`, `channels/` or `
|
|
104
|
-
//
|
|
105
|
-
// inside an agent's repository, which is the author's tree and not part of what that agent loads.
|
|
55
|
+
content: packageJson(`${toPackageName(dir)}-agent`, await fastagentVersion()),
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
// Inside another agent's DEFINITION (its `skills/`, `tools/`, `channels/` or `routines/`): the outer agent would
|
|
59
|
+
// load the new one as its own content.
|
|
106
60
|
const owner = agentDefinitionOwner(dir);
|
|
107
61
|
if (owner) {
|
|
108
62
|
throw new Error(`"${dir}" is inside the definition of the agent at ${owner} — an agent scaffolded here would be ` +
|
|
109
63
|
`part of THAT agent's surface, not one of its own. Init outside it.`);
|
|
110
64
|
}
|
|
111
|
-
// Preflight scaffold parent dirs FIRST: a pre-existing non-directory there would make mkdir fail
|
|
112
|
-
//
|
|
113
|
-
// must be named as such here, before the occupancy check below tries to read it as a directory
|
|
114
|
-
// (lstat, not stat: a symlinked parent must be rejected, not followed — it would write outside the
|
|
115
|
-
// agent dir).
|
|
65
|
+
// Preflight scaffold parent dirs FIRST: a pre-existing non-directory there would make mkdir fail mid-loop AFTER the
|
|
66
|
+
// first write, leaving a half-scaffold.
|
|
116
67
|
const parents = new Set();
|
|
117
68
|
for (const file of files) {
|
|
118
69
|
let p = dirname(file.rel);
|
|
@@ -127,52 +78,32 @@ export async function scaffoldAgent(dir, options = {}) {
|
|
|
127
78
|
throw new Error(`cannot scaffold: "${rel}" exists and is not a directory (a regular file or symlink) — remove it, or init elsewhere`);
|
|
128
79
|
}
|
|
129
80
|
}
|
|
130
|
-
// Refuse an occupied agent dir.
|
|
131
|
-
// message is actionable); any other content means an unfinished agent or something unrelated, and
|
|
132
|
-
// landing persona.md beside it would be a silent mix. Only ENOENT reads as "empty" — any other fault
|
|
133
|
-
// (EACCES…) must surface here rather than as a raw errno mid-write.
|
|
134
|
-
// (AGENTS.md outside is NOT a marker — it is context, adopted untouched.)
|
|
135
|
-
// `.DS_Store`/`.gitkeep`/`.keep` are not evidence of anyone's content: Finder noise, and the standard
|
|
136
|
-
// way to commit an empty directory (someone reserving the name in git ahead of init).
|
|
81
|
+
// Refuse an occupied agent dir.
|
|
137
82
|
const occupants = (await readdir(join(dir, root)).catch((e) => {
|
|
138
83
|
if (e.code === "ENOENT")
|
|
139
84
|
return [];
|
|
140
85
|
throw e;
|
|
141
86
|
})).filter((f) => ![".DS_Store", ".gitkeep", ".keep"].includes(f));
|
|
142
|
-
// Could `dir` still SELECT the agent this run creates?
|
|
143
|
-
// is a supported shape (different roles driving one repository), picked between by FASTAGENT_AGENT or
|
|
144
|
-
// the default name. A SHADOW is not: a config AT `dir` wins over everything inside it, so scaffolding
|
|
145
|
-
// under one (or scaffolding one over existing children) makes an agent the lookup can never return.
|
|
146
|
-
// The target itself is never in the way — a config already there means "already an agent", which the
|
|
147
|
-
// refusal below says in those words.
|
|
87
|
+
// Could `dir` still SELECT the agent this run creates?
|
|
148
88
|
const existing = agentsAt(dir).filter((a) => a !== resolve(dir, root));
|
|
149
|
-
const shadowed =
|
|
150
|
-
? existing // the new agent lands AT `dir` and hides everything inside it
|
|
151
|
-
: existing.filter((a) => a === resolve(dir)); // an agent AT `dir` hides the new one inside it
|
|
89
|
+
const shadowed = existing.filter((a) => a === resolve(dir)); // an agent AT `dir` hides the new one inside it
|
|
152
90
|
if (shadowed.length > 0) {
|
|
153
91
|
throw new Error(`"${dir}" already resolves to ${shadowed.map((a) => displayPath(process.cwd(), a) ?? a).join(", ")} — ` +
|
|
154
|
-
`an agent scaffolded
|
|
92
|
+
`an agent scaffolded in ./${root}/ would be hidden by it and never served ` +
|
|
155
93
|
`from "${dir}" (an agent AT a directory wins over any inside it). Use that agent, move it away, ` +
|
|
156
94
|
`or init in a different directory.`);
|
|
157
95
|
}
|
|
158
|
-
// ONE coordinate system for both refusals — `displayPath` is the shared policy (relative inside the
|
|
159
|
-
//
|
|
160
|
-
// one, in adjacent branches of the same command.
|
|
96
|
+
// ONE coordinate system for both refusals — `displayPath` is the shared policy (relative inside the cwd, absolute
|
|
97
|
+
// when it climbs out).
|
|
161
98
|
const target = displayPath(process.cwd(), join(dir, root)) ?? join(dir, root);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
throw new Error(`"${target}" already has ${config.join(", ")} — already a fastagent agent`);
|
|
99
|
+
if (occupants.includes(AGENT_CONFIG_FILE)) {
|
|
100
|
+
throw new Error(`"${target}" already has ${AGENT_CONFIG_FILE} — already a fastagent agent`);
|
|
165
101
|
}
|
|
166
|
-
|
|
167
|
-
// and every existing file is kept below.
|
|
168
|
-
if (!flat && occupants.length > 0) {
|
|
102
|
+
if (occupants.length > 0) {
|
|
169
103
|
throw new Error(`"${target}" already holds ${occupants.join(", ")} — move it away first, or run ` +
|
|
170
104
|
`\`fastagent init\` in a different directory`);
|
|
171
105
|
}
|
|
172
|
-
// Which directories were OURS to create?
|
|
173
|
-
// them (`--agent-dir .` adopts a directory that can already carry an empty `skills/` or `.secrets/`),
|
|
174
|
-
// and the rollback below must delete only what THIS run made. Recorded before the first write, since
|
|
175
|
-
// afterwards the two are indistinguishable.
|
|
106
|
+
// Which directories were OURS to create?
|
|
176
107
|
const preexisting = new Set();
|
|
177
108
|
for (const rel of parents)
|
|
178
109
|
if (await exists(join(dir, rel)))
|
|
@@ -180,42 +111,18 @@ export async function scaffoldAgent(dir, options = {}) {
|
|
|
180
111
|
const agentDirExisted = await exists(join(dir, root));
|
|
181
112
|
await mkdir(dir, { recursive: true });
|
|
182
113
|
const created = [];
|
|
183
|
-
|
|
184
|
-
//
|
|
185
|
-
// for them. Leaving the empty dirs behind would be worse than untidy: the occupancy refusal above
|
|
186
|
-
// would then report the next `init` as occupied, blaming the user for our own debris.
|
|
187
|
-
//
|
|
188
|
-
// `wx` never clobbers. A subdirectory target was proven empty, so EEXIST means a concurrent writer —
|
|
189
|
-
// an error. `.` is a directory being adopted, so an existing `.gitignore`/`package.json` is the
|
|
190
|
-
// author's — keep it and report it.
|
|
114
|
+
// ONE rollback scope: any failure removes what THIS run created — files AND the directories it made for them.
|
|
115
|
+
// `wx` so a collision is a failure: the target was checked empty above, so anything here appeared mid-run.
|
|
191
116
|
try {
|
|
192
117
|
for (const file of files) {
|
|
193
118
|
const abs = join(dir, file.rel);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
await ensureSecretsDir(dirname(abs));
|
|
198
|
-
else
|
|
199
|
-
await mkdir(dirname(abs), { recursive: true });
|
|
200
|
-
try {
|
|
201
|
-
await writeFile(abs, file.content, { flag: "wx" });
|
|
202
|
-
created.push(file.rel);
|
|
203
|
-
}
|
|
204
|
-
catch (e) {
|
|
205
|
-
if (!flat || e.code !== "EEXIST")
|
|
206
|
-
throw e;
|
|
207
|
-
kept.push(file.rel);
|
|
208
|
-
}
|
|
119
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
120
|
+
await writeFile(abs, file.content, { flag: "wx" });
|
|
121
|
+
created.push(file.rel);
|
|
209
122
|
}
|
|
210
123
|
}
|
|
211
124
|
catch (error) {
|
|
212
|
-
// Best-effort rollback of a partial scaffold
|
|
213
|
-
// original error below is the one worth surfacing — a cleanup failure must not mask it). Files
|
|
214
|
-
// first, then the directories THIS RUN created, deepest first; `rmdir` additionally removes only
|
|
215
|
-
// what is empty, so a pre-existing sibling inside one of ours is never touched. The agent root goes
|
|
216
|
-
// last, and only when this run created it — "empty" was never proof of ownership. (Reaching here at all takes a
|
|
217
|
-
// real fs fault: the occupancy refusal above proved the target empty, so nothing else can fail
|
|
218
|
-
// mid-loop. The covered case is a permission fault before the first write.)
|
|
125
|
+
// Best-effort rollback of a partial scaffold.
|
|
219
126
|
for (const rel of created.reverse())
|
|
220
127
|
await rm(join(dir, rel), { force: true }).catch(() => { });
|
|
221
128
|
for (const rel of [...parents].sort((a, b) => b.split(sep).length - a.split(sep).length)) {
|
|
@@ -226,5 +133,5 @@ export async function scaffoldAgent(dir, options = {}) {
|
|
|
226
133
|
await rmdir(join(dir, root)).catch(() => { });
|
|
227
134
|
throw error;
|
|
228
135
|
}
|
|
229
|
-
return { dir,
|
|
136
|
+
return { dir, agentDir: root, created };
|
|
230
137
|
}
|
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
# command: `FASTAGENT_AGENT=pm fastagent dev`.
|
|
15
15
|
|
|
16
16
|
# --- Model selection (overrides fastagent.config) ---
|
|
17
|
-
# Precedence: --model flag > FASTAGENT_MODEL > config
|
|
18
|
-
# fastagent.config; set it here to override
|
|
17
|
+
# Precedence: --model flag > FASTAGENT_MODEL > config — one chain, evaluated in whichever environment
|
|
18
|
+
# is running. `fastagent dev` writes your first-run pick to fastagent.config; set it here to override
|
|
19
|
+
# it. THIS FILE also declares the environment `fastagent deploy` resolves in, so a value here rides the
|
|
20
|
+
# release manifest into the deployment and a deployment can run a model the committed config does not
|
|
21
|
+
# name. That recorded value outranks the deployed box's own .env, so switching models there means
|
|
22
|
+
# editing this line and redeploying.
|
|
19
23
|
# FASTAGENT_MODEL=openai-codex/gpt-5.5
|
|
20
24
|
|
|
21
25
|
# --- Serving (fastagent start) ---
|
|
@@ -29,5 +33,3 @@
|
|
|
29
33
|
# the volume so a rotated OAuth credential persists across restarts. Set HERE it moves only
|
|
30
34
|
# auth.json — a value inside .env cannot relocate the .env being read.
|
|
31
35
|
# FASTAGENT_SECRETS_DIR=/data/.secrets
|
|
32
|
-
# Override just the sessions path (default: <state root>/sessions):
|
|
33
|
-
# FASTAGENT_SESSIONS_DIR=/data/sessions
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// fastagent.config.ts — deployment choices only (model / http; code tools auto-discover from tools/).
|
|
2
|
+
// Your agent's identity lives in persona.md; its capabilities in skills/ + tools/ — never here.
|
|
3
|
+
// An AGENTS.md in the WORKSPACE (the directory the agent is started in) is read as project context.
|
|
4
|
+
// Model precedence: `--model` flag > FASTAGENT_MODEL env > this default.
|
|
5
|
+
// No model is preset: `fastagent dev` shows the full model catalog (models you already have
|
|
6
|
+
// credentials for come first; picking one that needs auth logs you in inline) and writes your choice
|
|
7
|
+
// below. Or set it by hand to a "provider/modelId" (`fastagent models` lists them).
|
|
8
|
+
// Self-hosted model (vLLM/Ollama/…) or your own gateway? Declare it in a models.json next to this
|
|
9
|
+
// file and select it like any other spec — see docs/configuration.md "Custom model endpoints".
|
|
10
|
+
//
|
|
11
|
+
// `satisfies` is what makes your editor complete these keys and describe them on hover — and what
|
|
12
|
+
// turns a typo into an error you see while writing rather than one `fastagent dev` reports. The
|
|
13
|
+
// import is TYPE-only: nothing is loaded at runtime. Every key is optional; the commented lines
|
|
14
|
+
// below carry the default.
|
|
15
|
+
import type { FastagentConfig } from "@fastagent-sh/fastagent";
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
// model: "openai-codex/gpt-5.5",
|
|
19
|
+
// thinkingLevel: "high", // reasoning effort (off|minimal|low|medium|high|xhigh|max); default "medium" (pi TUI parity)
|
|
20
|
+
// add `host: "127.0.0.1"` here to pin the bind address; default: `start` all interfaces (what containers
|
|
21
|
+
// need), `dev` loopback. `cors: ["https://your-app.example.com"]` pins which origins a browser may call
|
|
22
|
+
// this serve from. The default is `*` on every bind — any page your users visit can call this port and
|
|
23
|
+
// read the reply, a loopback bind included (it stops another machine, not your own browser).
|
|
24
|
+
// `invoke: false` withholds POST /invoke, for a serve meant to be reached only through its channels
|
|
25
|
+
// (POST /run goes with it; `run: true` keeps that one for a scheduler of yours that calls routines by name).
|
|
26
|
+
http: { port: 8787 },
|
|
27
|
+
// sessionControl: true, // serve /control/* for remote observation + steering (a Web panel, a desktop app)
|
|
28
|
+
// tools: [], // programmatically defined tools, appended after the coding ones — tools/ is the usual way
|
|
29
|
+
// deploy: what the agent needs on the box (so `fastagent deploy` doesn't need a hand-written Dockerfile
|
|
30
|
+
// or hand-set host variables). Uncomment as needed:
|
|
31
|
+
// deploy: {
|
|
32
|
+
// secrets: ["GH_TOKEN"], // extra secret env vars your tools use — deploy reads their values from .secrets/.env
|
|
33
|
+
// apt: ["git"], // extra apt packages baked into the image (git, ripgrep, …; default repos only)
|
|
34
|
+
// agentcore: { idleTimeoutSeconds: 180 }, // `deploy agentcore` only: idle microVM tail, 60–1209600s
|
|
35
|
+
// },
|
|
36
|
+
} satisfies FastagentConfig;
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
export declare const baseTemplate: (name: string) => string;
|
|
3
3
|
/** Read one file from a channel's scaffold bundle (src/channels/<kind>/scaffold/<name>). */
|
|
4
4
|
export declare const channelTemplate: (kind: string, name: string) => string;
|
|
5
|
-
/** The .ts files in a channel's bundle: `channel
|
|
5
|
+
/** The .ts files in a channel's bundle: `channel.*` are the channel templates (one per ingress —
|
|
6
|
+
* `channel.ts`, `channel.websocket.ts`); the rest are companion tools. */
|
|
6
7
|
export declare const channelBundleFiles: (kind: string) => string[];
|
|
7
|
-
/** package.json for the complete agent.
|
|
8
|
-
* version, and tool authors use its `z` re-export rather than installing a second zod copy. */
|
|
8
|
+
/** package.json for the complete agent. */
|
|
9
9
|
export declare function packageJson(name: string, version: string): string;
|
|
10
10
|
/** Sanitize a directory basename into a valid npm package name (lowercase, safe chars). */
|
|
11
11
|
export declare function toPackageName(dir: string): string;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Scaffold template ACCESS (data, not logic): readers for the real files `init`/`add` write into an
|
|
3
|
-
*
|
|
4
|
-
* channel's bundle lives WITH the channel at ../channels/<kind>/scaffold/ (so a channel owns its
|
|
5
|
-
* starter kit and could ship as its own package). Both trees are excluded from this package's tsc +
|
|
6
|
-
* biome (they import the published @fastagent-sh/fastagent, not this source) and copied into dist/ by the build.
|
|
2
|
+
* Scaffold template ACCESS (data, not logic): readers for the real files `init`/`add` write into an agent dir, plus
|
|
3
|
+
* the parametric pieces.
|
|
7
4
|
*/
|
|
8
5
|
import { readFileSync, readdirSync } from "node:fs";
|
|
9
6
|
import { basename, resolve } from "node:path";
|
|
@@ -12,10 +9,10 @@ export const baseTemplate = (name) => readFileSync(new URL(`./templates/${name}`
|
|
|
12
9
|
const channelScaffoldDir = (kind) => new URL(`../channels/${kind}/scaffold/`, import.meta.url);
|
|
13
10
|
/** Read one file from a channel's scaffold bundle (src/channels/<kind>/scaffold/<name>). */
|
|
14
11
|
export const channelTemplate = (kind, name) => readFileSync(new URL(name, channelScaffoldDir(kind)), "utf8");
|
|
15
|
-
/** The .ts files in a channel's bundle: `channel
|
|
12
|
+
/** The .ts files in a channel's bundle: `channel.*` are the channel templates (one per ingress —
|
|
13
|
+
* `channel.ts`, `channel.websocket.ts`); the rest are companion tools. */
|
|
16
14
|
export const channelBundleFiles = (kind) => readdirSync(channelScaffoldDir(kind)).filter((f) => f.endsWith(".ts"));
|
|
17
|
-
/** package.json for the complete agent.
|
|
18
|
-
* version, and tool authors use its `z` re-export rather than installing a second zod copy. */
|
|
15
|
+
/** package.json for the complete agent. */
|
|
19
16
|
export function packageJson(name, version) {
|
|
20
17
|
return `${JSON.stringify({
|
|
21
18
|
name,
|
|
@@ -13,11 +13,8 @@ export interface VendoredSkill {
|
|
|
13
13
|
overwritten: boolean;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a
|
|
17
|
-
*
|
|
18
|
-
* Refuses to overwrite unless `options.update` (then a plain git-tracked overwrite, never a merge).
|
|
19
|
-
* Validates a staging copy with the runtime loader BEFORE replacing, so a bad fetch never destroys an
|
|
20
|
-
* existing skill.
|
|
16
|
+
* Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a local path, or a
|
|
17
|
+
* bare name (resolved against the local global skill dirs).
|
|
21
18
|
*/
|
|
22
19
|
export declare function vendorSkill(agentDir: string, source: string, options?: {
|
|
23
20
|
update?: boolean;
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `fastagent add skill <source>`: vendor an Agent Skills skill into `<agent dir>/skills/<name>/` —
|
|
3
|
-
*
|
|
4
|
-
* path, or a bare name (resolved against the local global skill dirs as an add-time copy source only).
|
|
5
|
-
* Fetch → staging → validate → rollback-protected replace, so a bad fetch never destroys an existing
|
|
6
|
-
* skill.
|
|
2
|
+
* `fastagent add skill <source>`: vendor an Agent Skills skill into `<agent dir>/skills/<name>/` — copy-in,
|
|
3
|
+
* git-tracked, never a runtime registry.
|
|
7
4
|
*/
|
|
8
5
|
import { cp, mkdir, readdir, rename, rm, stat } from "node:fs/promises";
|
|
9
6
|
import { existsSync } from "node:fs";
|
|
@@ -18,8 +15,8 @@ function skillNameFromSource(source) {
|
|
|
18
15
|
return basename(noRef.replace(/\/+$/, ""));
|
|
19
16
|
}
|
|
20
17
|
/**
|
|
21
|
-
* The skills/ path must be safe to write through: never follow a symlink that escapes the agent dir
|
|
22
|
-
*
|
|
18
|
+
* The skills/ path must be safe to write through: never follow a symlink that escapes the agent dir (mkdir would), and
|
|
19
|
+
* reject a plain file with one clear message.
|
|
23
20
|
*/
|
|
24
21
|
async function assertSkillsDirUsable(agentDir) {
|
|
25
22
|
await assertInsideAgentDir(agentDir, "skills");
|
|
@@ -37,8 +34,10 @@ function isLocalSource(source) {
|
|
|
37
34
|
function isBareName(source) {
|
|
38
35
|
return !source.includes("/") && !/^[a-z][a-z0-9+.-]*:/i.test(source);
|
|
39
36
|
}
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
37
|
+
/**
|
|
38
|
+
* Local "global" skill dirs, used ONLY as an add-time vendoring source (a bare `add skill <name>` copies the match in,
|
|
39
|
+
* git-tracked).
|
|
40
|
+
*/
|
|
42
41
|
function findGlobalSkillSource(name) {
|
|
43
42
|
for (const root of [join(homedir(), ".agents", "skills"), join(homedir(), ".pi", "agent", "skills")]) {
|
|
44
43
|
if (existsSync(join(root, name, "SKILL.md")))
|
|
@@ -47,11 +46,8 @@ function findGlobalSkillSource(name) {
|
|
|
47
46
|
return undefined;
|
|
48
47
|
}
|
|
49
48
|
/**
|
|
50
|
-
* Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a
|
|
51
|
-
*
|
|
52
|
-
* Refuses to overwrite unless `options.update` (then a plain git-tracked overwrite, never a merge).
|
|
53
|
-
* Validates a staging copy with the runtime loader BEFORE replacing, so a bad fetch never destroys an
|
|
54
|
-
* existing skill.
|
|
49
|
+
* Vendor an Agent Skills skill into `<agent dir>/skills/<name>/` from a giget ref (github default), a local path, or a
|
|
50
|
+
* bare name (resolved against the local global skill dirs).
|
|
55
51
|
*/
|
|
56
52
|
export async function vendorSkill(agentDir, source, options = {}) {
|
|
57
53
|
const name = skillNameFromSource(source);
|
|
@@ -61,8 +57,7 @@ export async function vendorSkill(agentDir, source, options = {}) {
|
|
|
61
57
|
const skillsDir = join(agentDir, "skills");
|
|
62
58
|
await assertSkillsDirUsable(agentDir);
|
|
63
59
|
const dest = join(skillsDir, name);
|
|
64
|
-
// A process can die between moving the old skill aside and installing staging.
|
|
65
|
-
// arbitrary hidden directory is ours or delete it: stop with the exact backup path for manual restore.
|
|
60
|
+
// A process can die between moving the old skill aside and installing staging.
|
|
66
61
|
const skillEntries = await readdir(skillsDir).catch((error) => {
|
|
67
62
|
if (error.code === "ENOENT")
|
|
68
63
|
return [];
|
|
@@ -72,16 +67,15 @@ export async function vendorSkill(agentDir, source, options = {}) {
|
|
|
72
67
|
if (interrupted.length > 0) {
|
|
73
68
|
throw new Error(`found an interrupted skill update backup (${interrupted.join(", ")}) — inspect it, then restore it to skills/${name} or remove it before retrying`);
|
|
74
69
|
}
|
|
75
|
-
// Refuse to clobber unless --update; a git-tracked overwrite is safe (review with `git diff`, undo
|
|
76
|
-
//
|
|
70
|
+
// Refuse to clobber unless --update; a git-tracked overwrite is safe (review with `git diff`, undo with `git
|
|
71
|
+
// checkout`).
|
|
77
72
|
const overwritten = existsSync(dest);
|
|
78
73
|
if (overwritten && !options.update) {
|
|
79
74
|
throw new Error(`skills/${name} already exists — re-run with --update to overwrite it (git tracks the change), or remove it`);
|
|
80
75
|
}
|
|
81
76
|
await mkdir(skillsDir, { recursive: true });
|
|
82
|
-
// Fetch into a STAGING dir (same filesystem → atomic rename), validate, and only THEN replace dest,
|
|
83
|
-
//
|
|
84
|
-
// treating staging as a skill.
|
|
77
|
+
// Fetch into a STAGING dir (same filesystem → atomic rename), validate, and only THEN replace dest, so a
|
|
78
|
+
// failed/invalid fetch never destroys an existing skill.
|
|
85
79
|
const staging = join(skillsDir, `.${name}.vendoring`);
|
|
86
80
|
await rm(staging, { recursive: true, force: true }); // clear any leftover from a prior crash
|
|
87
81
|
try {
|
|
@@ -102,9 +96,7 @@ export async function vendorSkill(agentDir, source, options = {}) {
|
|
|
102
96
|
await cp(src, staging, { recursive: true });
|
|
103
97
|
}
|
|
104
98
|
else {
|
|
105
|
-
// giget defaults a BARE ref to its own template registry, not github
|
|
106
|
-
// github for a plain `owner/repo/path` (an explicit `github:`/`gh:`/`gitlab:`… scheme is kept).
|
|
107
|
-
// Supports a subdir + #ref, fetched via the tar API (no git binary).
|
|
99
|
+
// giget defaults a BARE ref to its own template registry, not github.
|
|
108
100
|
const ref = /^[a-z][a-z0-9+.-]*:/i.test(source) ? source : `github:${source}`;
|
|
109
101
|
// Lazy import: giget is only needed for a git ref, so the serve path (index.ts → init.ts) and the
|
|
110
102
|
// local/bare-name sources never load it.
|
|
@@ -119,9 +111,7 @@ export async function vendorSkill(agentDir, source, options = {}) {
|
|
|
119
111
|
await rm(staging, { recursive: true, force: true }); // failed/invalid: drop staging, leave dest intact
|
|
120
112
|
throw error;
|
|
121
113
|
}
|
|
122
|
-
// Install with rollback: move the old skill to a unique backup, then put staging in place.
|
|
123
|
-
// second rename restores it; a process crash leaves a backup the next invocation reports without
|
|
124
|
-
// touching it.
|
|
114
|
+
// Install with rollback: move the old skill to a unique backup, then put staging in place.
|
|
125
115
|
const previous = join(skillsDir, `.${name}.previous-${randomUUID()}`);
|
|
126
116
|
if (overwritten)
|
|
127
117
|
await rename(dest, previous);
|
|
@@ -141,8 +131,8 @@ export async function vendorSkill(agentDir, source, options = {}) {
|
|
|
141
131
|
}
|
|
142
132
|
if (overwritten)
|
|
143
133
|
await rm(previous, { recursive: true, force: true });
|
|
144
|
-
// Report via the runtime loader, matching THIS skill by EXACT directory (a substring match would
|
|
145
|
-
//
|
|
134
|
+
// Report via the runtime loader, matching THIS skill by EXACT directory (a substring match would prefix-pollute a
|
|
135
|
+
// sibling `<name>-x` and break on Windows path separators).
|
|
146
136
|
const def = await loadAgentDefinition(agentDir);
|
|
147
137
|
const rel = join("skills", name);
|
|
148
138
|
const skill = def.skills.find((sk) => relative(agentDir, dirname(sk.filePath)) === rel);
|
package/dist/schedule/cron.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
1
|
+
/**
|
|
2
|
+
* The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the expression will never
|
|
3
|
+
* fire again.
|
|
4
|
+
*/
|
|
3
5
|
export declare function nextRun(cron: string, tz: string | undefined, from: Date): Date | undefined;
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
/**
|
|
7
|
+
* Why `cron`/`tz` is invalid (for load-time validation), or undefined if valid. croner validates the timezone lazily
|
|
8
|
+
* (not at construction), so check it explicitly via Intl (throws on an unknown IANA zone); the pattern is validated by
|
|
9
|
+
* constructing the Cron.
|
|
10
|
+
*/
|
|
7
11
|
export declare function cronError(cron: string, tz: string | undefined): string | undefined;
|
package/dist/schedule/cron.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
+
/** The one place that touches the cron library (`croner`). */
|
|
2
|
+
import { Cron } from "croner";
|
|
1
3
|
/**
|
|
2
|
-
* The
|
|
3
|
-
*
|
|
4
|
-
* scheduling. croner is chosen for zero transitive dependencies + built-in IANA timezone/DST handling
|
|
5
|
-
* (wall-clock schedules like "daily 9am America/New_York" need DST-correct arithmetic that hand-rolling
|
|
6
|
-
* gets wrong at the spring-forward / fall-back boundary).
|
|
4
|
+
* The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the expression will never
|
|
5
|
+
* fire again.
|
|
7
6
|
*/
|
|
8
|
-
import { Cron } from "croner";
|
|
9
|
-
/** The next scheduled instant STRICTLY AFTER `from` (in `tz`, default UTC), or undefined if the
|
|
10
|
-
* expression will never fire again. */
|
|
11
7
|
export function nextRun(cron, tz, from) {
|
|
12
8
|
return new Cron(cron, { timezone: tz ?? "UTC" }).nextRun(from) ?? undefined;
|
|
13
9
|
}
|
|
14
|
-
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
/**
|
|
11
|
+
* Why `cron`/`tz` is invalid (for load-time validation), or undefined if valid. croner validates the timezone lazily
|
|
12
|
+
* (not at construction), so check it explicitly via Intl (throws on an unknown IANA zone); the pattern is validated by
|
|
13
|
+
* constructing the Cron.
|
|
14
|
+
*/
|
|
17
15
|
export function cronError(cron, tz) {
|
|
18
16
|
if (tz !== undefined) {
|
|
19
17
|
try {
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { type ModuleLoadFailure } from "../loader.ts";
|
|
2
|
-
import type
|
|
3
|
-
|
|
4
|
-
* (deploy pre-flight's time-trigger detection; `fastagent info` uses {@link loadSchedules} instead,
|
|
5
|
-
* since it also reports broken files and next instants). */
|
|
6
|
-
export declare function discoverScheduleFiles(dir: string): Promise<string[]>;
|
|
2
|
+
import { type DeclaredSecret } from "../declared-secrets.ts";
|
|
3
|
+
import type { LoadedRoutine } from "./routine.ts";
|
|
7
4
|
/**
|
|
8
|
-
* Discover
|
|
9
|
-
*
|
|
10
|
-
* an invalid cron/tz — is ISOLATED into `failures` (skipped + reported, never a crash-loop, G2), the same
|
|
11
|
-
* way `loadTools`/`loadChannels` isolate theirs. A duplicate name (foo.ts + foo.js) keeps the first and
|
|
12
|
-
* reports the rest.
|
|
5
|
+
* Discover routines in `<dir>/routines/`: each file default-exports a `defineRoutine({...})`, named from its
|
|
6
|
+
* filename.
|
|
13
7
|
*/
|
|
14
|
-
export declare function
|
|
15
|
-
|
|
8
|
+
export declare function loadRoutines(dir: string): Promise<{
|
|
9
|
+
routines: LoadedRoutine[];
|
|
10
|
+
/** What each loaded routine declared it needs, BY ROUTINE NAME and attributed to its file. Per
|
|
11
|
+
* routine because a caller that runs exactly ONE of them (`fastagent routine run`) must not be stopped
|
|
12
|
+
* by a sibling's credential, and because two routines declaring the same variable would
|
|
13
|
+
* otherwise blame whichever file was read first. The clock flattens it (it runs all of them).
|
|
14
|
+
* Data, not a check — only a serving path asserts it (src/declared-secrets.ts). */
|
|
15
|
+
secrets: Map<string, DeclaredSecret[]>;
|
|
16
16
|
failures: ModuleLoadFailure[];
|
|
17
17
|
}>;
|