@fastagent-sh/fastagent 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/dist/atomic-write.d.ts +4 -5
- package/dist/atomic-write.js +4 -5
- package/dist/authoring.d.ts +32 -15
- package/dist/authoring.js +147 -127
- package/dist/channels/agentcore-protocol.d.ts +8 -3
- package/dist/channels/agentcore-protocol.js +3 -1
- package/dist/channels/agentcore-service.d.ts +1 -0
- package/dist/channels/agentcore-service.js +14 -4
- package/dist/channels/agentcore.d.ts +9 -0
- package/dist/channels/agentcore.js +29 -4
- package/dist/channels/context.d.ts +2 -0
- package/dist/channels/context.js +5 -1
- package/dist/channels/feishu/feishu-api.d.ts +3 -1
- package/dist/channels/feishu/feishu-api.js +4 -0
- package/dist/channels/feishu/feishu.d.ts +0 -3
- package/dist/channels/feishu/feishu.js +10 -10
- package/dist/channels/feishu/register-app.d.ts +2 -0
- package/dist/channels/feishu/register-app.js +4 -6
- package/dist/channels/feishu/scaffold/channel.ts +32 -34
- package/dist/channels/feishu/scaffold/feishu-send.ts +5 -5
- package/dist/channels/feishu/scaffold/feishu-threads.ts +3 -3
- package/dist/channels/feishu/setup-mode.d.ts +2 -12
- package/dist/channels/feishu/setup-mode.js +2 -22
- package/dist/channels/feishu/setup.d.ts +12 -0
- package/dist/channels/feishu/setup.js +197 -0
- package/dist/channels/kit/abortable.d.ts +8 -0
- package/dist/channels/kit/abortable.js +22 -0
- package/dist/channels/lark/lark.d.ts +0 -3
- package/dist/channels/lark/lark.js +0 -5
- package/dist/channels/lark/scaffold/channel.ts +33 -35
- package/dist/channels/lark/scaffold/lark-send.ts +5 -5
- package/dist/channels/lark/scaffold/lark-threads.ts +3 -3
- package/dist/channels/lark/setup.d.ts +9 -0
- package/dist/channels/lark/setup.js +57 -0
- package/dist/channels/setup.d.ts +112 -0
- package/dist/channels/setup.js +31 -0
- package/dist/channels/slack/history.js +1 -1
- package/dist/channels/slack/manifest.d.ts +17 -7
- package/dist/channels/slack/manifest.js +12 -16
- package/dist/channels/slack/scaffold/channel.ts +44 -30
- package/dist/channels/slack/scaffold/slack-send.ts +8 -8
- package/dist/channels/slack/scaffold/slack-threads.ts +4 -4
- package/dist/channels/slack/setup.d.ts +7 -0
- package/dist/channels/slack/setup.js +119 -0
- package/dist/channels/slack/slack-api.d.ts +2 -0
- package/dist/channels/slack/slack-api.js +4 -0
- package/dist/channels/slack/slack.d.ts +21 -2
- package/dist/channels/slack/slack.js +398 -338
- package/dist/channels/slack/socket.d.ts +57 -0
- package/dist/channels/slack/socket.js +305 -0
- package/dist/channels/telegram/polling.d.ts +27 -0
- package/dist/channels/telegram/polling.js +105 -0
- package/dist/channels/telegram/scaffold/channel.ts +52 -32
- package/dist/channels/telegram/scaffold/telegram-send.ts +9 -9
- package/dist/channels/telegram/setup.d.ts +2 -0
- package/dist/channels/telegram/setup.js +42 -0
- package/dist/channels/telegram/telegram-api.d.ts +27 -1
- package/dist/channels/telegram/telegram-api.js +7 -6
- package/dist/channels/telegram/telegram.d.ts +11 -3
- package/dist/channels/telegram/telegram.js +184 -157
- package/dist/channels/wait-health.js +1 -1
- package/dist/channels/webhook-evidence.d.ts +11 -0
- package/dist/channels/webhook-evidence.js +71 -0
- package/dist/channels/webhooks.d.ts +37 -0
- package/dist/channels/webhooks.js +70 -0
- package/dist/child-env.d.ts +16 -0
- package/dist/child-env.js +30 -0
- package/dist/cli/box-login.js +4 -1
- package/dist/cli/channel-app.d.ts +3 -0
- package/dist/cli/channel-app.js +35 -0
- package/dist/cli/channel-setup.d.ts +38 -0
- package/dist/cli/channel-setup.js +195 -0
- package/dist/cli/commands/add.d.ts +6 -14
- package/dist/cli/commands/add.js +45 -310
- package/dist/cli/commands/auth.js +8 -2
- package/dist/cli/commands/build.d.ts +5 -0
- package/dist/cli/commands/build.js +16 -0
- package/dist/cli/commands/channels.d.ts +19 -0
- package/dist/cli/commands/channels.js +230 -0
- package/dist/cli/commands/content.d.ts +7 -3
- package/dist/cli/commands/content.js +16 -10
- package/dist/cli/commands/deploy/agentcore.js +55 -23
- package/dist/cli/commands/deploy/channel-apps.d.ts +17 -0
- package/dist/cli/commands/deploy/channel-apps.js +30 -0
- package/dist/cli/commands/deploy/docker.js +32 -65
- package/dist/cli/commands/deploy/fly.js +28 -12
- package/dist/cli/commands/deploy/railway.js +38 -13
- package/dist/cli/commands/deploy/secrets-step.d.ts +19 -0
- package/dist/cli/commands/deploy/secrets-step.js +66 -0
- package/dist/cli/commands/deploy/shared.d.ts +16 -5
- package/dist/cli/commands/deploy/shared.js +25 -8
- package/dist/cli/commands/deploy.js +53 -67
- package/dist/cli/commands/dev.d.ts +2 -0
- package/dist/cli/commands/dev.js +15 -3
- package/dist/cli/commands/env.js +1 -1
- package/dist/cli/commands/info.d.ts +2 -0
- package/dist/cli/commands/info.js +63 -24
- package/dist/cli/commands/init.js +3 -1
- package/dist/cli/commands/invoke.d.ts +2 -0
- package/dist/cli/commands/invoke.js +20 -7
- package/dist/cli/commands/login.d.ts +0 -7
- package/dist/cli/commands/login.js +22 -10
- package/dist/cli/commands/logout.js +1 -1
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +164 -0
- package/dist/cli/commands/models.js +3 -2
- package/dist/cli/commands/schedules.d.ts +4 -2
- package/dist/cli/commands/schedules.js +14 -10
- package/dist/cli/commands/secrets.d.ts +14 -0
- package/dist/cli/commands/secrets.js +213 -0
- package/dist/cli/commands/start.d.ts +14 -14
- package/dist/cli/commands/start.js +46 -60
- package/dist/cli/content-view.d.ts +4 -6
- package/dist/cli/content-view.js +5 -11
- package/dist/cli/first-run-channels.d.ts +3 -0
- package/dist/cli/first-run-channels.js +63 -0
- package/dist/cli/instance.d.ts +41 -0
- package/dist/cli/instance.js +155 -0
- package/dist/cli/invoke-stream.d.ts +5 -2
- package/dist/cli/invoke-stream.js +7 -2
- package/dist/cli/program.js +339 -108
- package/dist/cli/serve.d.ts +4 -5
- package/dist/cli/serve.js +4 -7
- package/dist/cli/shared.d.ts +11 -2
- package/dist/cli/shared.js +32 -19
- package/dist/cli/ticket.js +50 -6
- package/dist/content/declare.d.ts +31 -37
- package/dist/content/declare.js +59 -60
- package/dist/content/file.d.ts +23 -12
- package/dist/content/file.js +48 -35
- package/dist/content/git.d.ts +0 -10
- package/dist/content/git.js +5 -39
- package/dist/content/resolve.d.ts +18 -22
- package/dist/content/resolve.js +32 -66
- package/dist/content/source.d.ts +7 -8
- package/dist/content/source.js +10 -28
- package/dist/credentials-page.d.ts +26 -0
- package/dist/credentials-page.js +185 -0
- package/dist/declared-secrets.d.ts +10 -3
- package/dist/declared-secrets.js +8 -4
- package/dist/deploy/agentcore/destroy.d.ts +4 -1
- package/dist/deploy/agentcore/destroy.js +26 -2
- package/dist/deploy/agentcore/forwarder.js +1 -1
- package/dist/deploy/agentcore/plan.d.ts +7 -21
- package/dist/deploy/agentcore/plan.js +47 -49
- package/dist/deploy/agentcore/run.d.ts +18 -10
- package/dist/deploy/agentcore/run.js +93 -70
- package/dist/deploy/agentcore/runtime-secrets.d.ts +7 -0
- package/dist/deploy/agentcore/runtime-secrets.js +108 -0
- package/dist/deploy/agentcore/shell.js +12 -19
- package/dist/deploy/agentcore/sigv4.d.ts +7 -0
- package/dist/deploy/agentcore/sigv4.js +15 -0
- package/dist/deploy/agentcore/slr.d.ts +18 -0
- package/dist/deploy/agentcore/slr.js +27 -0
- package/dist/deploy/agentcore/store.d.ts +7 -0
- package/dist/deploy/agentcore/store.js +115 -0
- package/dist/deploy/api.d.ts +14 -14
- package/dist/deploy/api.js +26 -24
- package/dist/deploy/box-shell.d.ts +3 -1
- package/dist/deploy/build-context.d.ts +4 -4
- package/dist/deploy/build-context.js +11 -11
- package/dist/deploy/channel-ingress.d.ts +35 -9
- package/dist/deploy/channel-ingress.js +119 -22
- package/dist/deploy/container.d.ts +35 -10
- package/dist/deploy/container.js +80 -39
- package/dist/deploy/delivered-text.d.ts +13 -0
- package/dist/deploy/delivered-text.js +86 -0
- package/dist/deploy/docker/plan.d.ts +7 -8
- package/dist/deploy/docker/plan.js +31 -52
- package/dist/deploy/docker/run.d.ts +18 -43
- package/dist/deploy/docker/run.js +57 -79
- package/dist/deploy/file-store.d.ts +2 -0
- package/dist/deploy/file-store.js +17 -0
- package/dist/deploy/fly/plan.d.ts +3 -2
- package/dist/deploy/fly/plan.js +20 -9
- package/dist/deploy/fly/run.d.ts +18 -22
- package/dist/deploy/fly/run.js +25 -44
- package/dist/deploy/fly/store.d.ts +12 -0
- package/dist/deploy/fly/store.js +83 -0
- package/dist/deploy/hosts.d.ts +1 -2
- package/dist/deploy/hosts.js +1 -2
- package/dist/deploy/preflight.d.ts +18 -5
- package/dist/deploy/preflight.js +135 -63
- package/dist/deploy/railway/ignore.d.ts +8 -0
- package/dist/deploy/railway/ignore.js +57 -0
- package/dist/deploy/railway/plan.d.ts +1 -2
- package/dist/deploy/railway/plan.js +8 -6
- package/dist/deploy/railway/run.d.ts +21 -15
- package/dist/deploy/railway/run.js +32 -78
- package/dist/deploy/railway/store.d.ts +29 -0
- package/dist/deploy/railway/store.js +143 -0
- package/dist/deploy/runner.d.ts +5 -2
- package/dist/deploy/runner.js +4 -3
- package/dist/deploy/secret-store.d.ts +63 -0
- package/dist/deploy/secret-store.js +98 -0
- package/dist/deploy/secrets.d.ts +29 -46
- package/dist/deploy/secrets.js +49 -122
- package/dist/deploy/workspace.d.ts +6 -12
- package/dist/deploy/workspace.js +18 -94
- package/dist/dev-supervisor.js +6 -7
- package/dist/environment/declare.js +3 -2
- package/dist/environment/mise.d.ts +3 -1
- package/dist/environment/mise.js +4 -2
- package/dist/feishu.d.ts +1 -1
- package/dist/feishu.js +1 -1
- package/dist/harnesses/models/catalog.d.ts +1 -0
- package/dist/harnesses/models/catalog.js +1 -2
- package/dist/harnesses/models/environment.d.ts +7 -2
- package/dist/harnesses/models/environment.js +9 -5
- package/dist/harnesses/models/host.d.ts +0 -7
- package/dist/harnesses/models/host.js +10 -1
- package/dist/harnesses/models/login.d.ts +0 -8
- package/dist/harnesses/models/login.js +5 -4
- package/dist/harnesses/models/models.d.ts +7 -2
- package/dist/harnesses/models/models.js +28 -4
- package/dist/harnesses/pi-durable/agent.js +59 -38
- package/dist/harnesses/pi-durable/build.d.ts +12 -0
- package/dist/harnesses/pi-durable/build.js +270 -0
- package/dist/harnesses/pi-durable/built.d.ts +24 -0
- package/dist/harnesses/pi-durable/built.js +136 -0
- package/dist/harnesses/pi-durable/config.d.ts +81 -0
- package/dist/harnesses/pi-durable/config.js +266 -0
- package/dist/harnesses/pi-durable/define.d.ts +63 -13
- package/dist/harnesses/pi-durable/define.js +23 -2
- package/dist/harnesses/pi-durable/definition.d.ts +72 -0
- package/dist/harnesses/pi-durable/definition.js +143 -0
- package/dist/harnesses/pi-durable/events.d.ts +4 -3
- package/dist/harnesses/pi-durable/events.js +179 -136
- package/dist/harnesses/pi-durable/execution-env.d.ts +17 -0
- package/dist/harnesses/pi-durable/execution-env.js +11 -0
- package/dist/harnesses/pi-durable/mcp.d.ts +3 -0
- package/dist/harnesses/pi-durable/mcp.js +32 -0
- package/dist/harnesses/pi-durable/open.d.ts +36 -38
- package/dist/harnesses/pi-durable/open.js +97 -150
- package/dist/harnesses/pi-durable/reads.d.ts +0 -1
- package/dist/harnesses/pi-durable/reads.js +1 -3
- package/dist/harnesses/pi-durable/registry.d.ts +4 -6
- package/dist/harnesses/pi-durable/registry.js +29 -26
- package/dist/harnesses/pi-durable/release.d.ts +2 -2
- package/dist/harnesses/pi-durable/release.js +10 -7
- package/dist/harnesses/pi-durable/serve.d.ts +95 -15
- package/dist/harnesses/pi-durable/serve.js +151 -59
- package/dist/harnesses/pi-durable/sessions.js +15 -12
- package/dist/harnesses/pi-durable/text-sources.d.ts +30 -0
- package/dist/harnesses/pi-durable/text-sources.js +55 -0
- package/dist/harnesses/pi-durable/text.d.ts +16 -4
- package/dist/harnesses/pi-durable/text.js +115 -59
- package/dist/harnesses/pi-durable/wake.js +8 -8
- package/dist/http/client.js +9 -3
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.js +8 -1
- package/dist/instance-name.d.ts +7 -0
- package/dist/instance-name.js +26 -0
- package/dist/lark.d.ts +1 -1
- package/dist/lark.js +1 -1
- package/dist/locked-file.d.ts +2 -2
- package/dist/locked-file.js +8 -3
- package/dist/mcp/auth.d.ts +22 -0
- package/dist/mcp/auth.js +180 -0
- package/dist/mcp/client.d.ts +15 -0
- package/dist/mcp/client.js +179 -0
- package/dist/mcp/config.d.ts +86 -0
- package/dist/mcp/config.js +225 -0
- package/dist/mcp/grants.d.ts +4 -0
- package/dist/mcp/grants.js +132 -0
- package/dist/mcp/host.d.ts +19 -0
- package/dist/mcp/host.js +128 -0
- package/dist/mcp/network.d.ts +12 -0
- package/dist/mcp/network.js +150 -0
- package/dist/mcp/sign-in.d.ts +2 -0
- package/dist/mcp/sign-in.js +204 -0
- package/dist/mcp-api.d.ts +107 -0
- package/dist/mcp-api.js +10 -0
- package/dist/paths.d.ts +22 -8
- package/dist/paths.js +44 -11
- package/dist/runtime-websocket.d.ts +14 -0
- package/dist/runtime-websocket.js +39 -0
- package/dist/scaffold/add-channel.d.ts +9 -16
- package/dist/scaffold/add-channel.js +13 -83
- package/dist/scaffold/templates/env.example +4 -5
- package/dist/scaffold/templates/fastagent.config.ts +20 -14
- package/dist/scaffold/templates/gitignore +9 -6
- package/dist/scaffold/templates.d.ts +6 -2
- package/dist/scaffold/templates.js +7 -2
- package/dist/scaffold/vendor-skill.js +4 -2
- package/dist/service.d.ts +44 -6
- package/dist/service.js +110 -10
- package/dist/serving-command.d.ts +10 -0
- package/dist/serving-command.js +1 -0
- package/dist/setup-page.d.ts +51 -0
- package/dist/setup-page.js +149 -0
- package/dist/slack.d.ts +1 -1
- package/dist/slack.js +1 -1
- package/dist/strip-edges.d.ts +10 -0
- package/dist/strip-edges.js +22 -0
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +1 -1
- package/dist/tunnel.d.ts +0 -1
- package/dist/tunnel.js +2 -8
- package/dist/value-file.d.ts +12 -6
- package/dist/value-file.js +36 -25
- package/dist/version.d.ts +7 -1
- package/dist/version.js +9 -3
- package/package.json +8 -6
- package/dist/channels/lark/onboard.d.ts +0 -36
- package/dist/channels/lark/onboard.js +0 -56
- package/dist/channels/slack/config-api.d.ts +0 -69
- package/dist/channels/slack/config-api.js +0 -162
- package/dist/channels/slack/onboard.d.ts +0 -40
- package/dist/channels/slack/onboard.js +0 -136
- package/dist/channels/slack/onboarding-state.d.ts +0 -31
- package/dist/channels/slack/onboarding-state.js +0 -64
- package/dist/channels/slack/register-webhook.d.ts +0 -12
- package/dist/channels/slack/register-webhook.js +0 -67
- package/dist/channels/slack/setup-server.d.ts +0 -32
- package/dist/channels/slack/setup-server.js +0 -201
- package/dist/cli/add-feishu.d.ts +0 -50
- package/dist/cli/add-feishu.js +0 -367
- package/dist/cli/add-slack.d.ts +0 -18
- package/dist/cli/add-slack.js +0 -216
- package/dist/harnesses/pi-durable/parked.d.ts +0 -18
- package/dist/harnesses/pi-durable/parked.js +0 -27
package/dist/content/file.d.ts
CHANGED
|
@@ -1,32 +1,43 @@
|
|
|
1
1
|
import { type DeclaredEnvironment } from "../environment/declare.ts";
|
|
2
|
-
import { type
|
|
2
|
+
import { type ContentEntry, type DeclaredContent } from "./declare.ts";
|
|
3
3
|
/** `context.json` as read: the content entries as written and as declared, and the environment. */
|
|
4
4
|
export interface ContextFile {
|
|
5
5
|
$schema?: string;
|
|
6
6
|
content: Record<string, ContentEntry>;
|
|
7
7
|
declared: DeclaredContent[];
|
|
8
8
|
environment?: DeclaredEnvironment;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export interface LocalContextFile {
|
|
12
|
-
content: Record<string, ContentBinding>;
|
|
13
|
-
bound: BoundContent[];
|
|
9
|
+
/** The program's MCP servers as written. Their shape is the MCP reader's to check; an edit keeps them as they are. */
|
|
10
|
+
mcpServers?: Record<string, unknown>;
|
|
14
11
|
}
|
|
15
12
|
/** Read `text`, the contents of `path`, whole: what an edit, which writes the whole, reads. */
|
|
16
13
|
export declare function parseContextFile(text: string, path: string): ContextFile;
|
|
17
|
-
/** Read `text`, the contents of `path`, whole, beside the entries `context.json` declares. */
|
|
18
|
-
export declare function parseLocalContextFile(text: string, path: string, declared: readonly DeclaredContent[], agentDir: string): LocalContextFile;
|
|
19
14
|
/** The agent's `context.json`, read whole. */
|
|
20
15
|
export declare function readContextFile(agentDir: string): ContextFile;
|
|
21
|
-
/** The content
|
|
22
|
-
export
|
|
16
|
+
/** The instance configuration's content, as far as this reads it: its `content` map and where it is from. */
|
|
17
|
+
export interface InstanceContent {
|
|
18
|
+
values: unknown;
|
|
19
|
+
label: string;
|
|
20
|
+
/** The configuration file's directory, which a relative `path` is relative to. */
|
|
21
|
+
directory?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The content of the agent whose program is in `agentDir`: what its `context.json` declares, and what `instance` adds
|
|
25
|
+
* to it (an instance only adds: a name both declare is refused).
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadContent(agentDir: string, instance?: InstanceContent): DeclaredContent[];
|
|
28
|
+
/** What an instance configuration's `content` declares. */
|
|
29
|
+
export declare function instanceContent({ values, label, directory }: InstanceContent): DeclaredContent[];
|
|
23
30
|
/** The environment the agent in `agentDir` declares, or undefined when it declares none. */
|
|
24
31
|
export declare function loadEnvironment(agentDir: string): DeclaredEnvironment | undefined;
|
|
32
|
+
/** The program's `mcpServers` map as written in `context.json`, and the file's path, which names it in a refusal. */
|
|
33
|
+
export declare function readProgramMcpServers(agentDir: string): {
|
|
34
|
+
values: unknown;
|
|
35
|
+
path: string;
|
|
36
|
+
};
|
|
25
37
|
/** The text `context.json` is written as. An environment that declares nothing is left out. */
|
|
26
38
|
export declare function contextFileText(file: {
|
|
27
39
|
$schema?: string;
|
|
28
40
|
content: Record<string, ContentEntry>;
|
|
29
41
|
environment?: DeclaredEnvironment;
|
|
42
|
+
mcpServers?: Record<string, unknown>;
|
|
30
43
|
}): string;
|
|
31
|
-
/** The text `context.local.json` is written as. */
|
|
32
|
-
export declare function localContextFileText(content: Record<string, ContentBinding>): string;
|
package/dist/content/file.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The
|
|
3
|
-
* `content list`, `env`, `tool` and deploy read one declaration; and the one shape
|
|
4
|
-
* declares the part it uses (its content, or its environment), so a part that is refused stops what uses
|
|
5
|
-
* reported where it is, not as the other's; an edit declares the whole, since it writes the whole. JSON,
|
|
6
|
-
* `mcp.json` and `package.json`, so a command, a client or the agent itself can edit it without touching
|
|
2
|
+
* The program's `context.json` and the content an instance's configuration adds, read in ONE place, so the opener,
|
|
3
|
+
* `info`, `content list`, `env`, `tool` and deploy read one declaration; and the one shape `context.json` is written
|
|
4
|
+
* in. A reader declares the part it uses (its content, or its environment), so a part that is refused stops what uses
|
|
5
|
+
* it and is reported where it is, not as the other's; an edit declares the whole, since it writes the whole. JSON,
|
|
6
|
+
* like `mcp.json` and `package.json`, so a command, a client or the agent itself can edit it without touching
|
|
7
|
+
* TypeScript.
|
|
7
8
|
*/
|
|
8
9
|
import { readFileSync } from "node:fs";
|
|
9
10
|
import { join, resolve } from "node:path";
|
|
10
|
-
import { CONTEXT_FILE
|
|
11
|
+
import { CONTEXT_FILE } from "../paths.js";
|
|
11
12
|
import { declareEnvironment } from "../environment/declare.js";
|
|
12
|
-
import {
|
|
13
|
-
const FILE_KEYS = ["$schema", "content", "environment"];
|
|
14
|
-
const LOCAL_FILE_KEYS = ["content"];
|
|
13
|
+
import { declareContent, joinContent } from "./declare.js";
|
|
14
|
+
const FILE_KEYS = ["$schema", "content", "environment", "mcpServers"];
|
|
15
15
|
/** `text`, the contents of `path`, as the object a context file is: refused when the file itself is wrong. */
|
|
16
|
-
function parseFile(text, path
|
|
16
|
+
function parseFile(text, path) {
|
|
17
17
|
let raw;
|
|
18
18
|
try {
|
|
19
19
|
raw = JSON.parse(text);
|
|
@@ -25,12 +25,17 @@ function parseFile(text, path, keys = FILE_KEYS) {
|
|
|
25
25
|
throw new Error(`${path} must hold a JSON object`);
|
|
26
26
|
const file = raw;
|
|
27
27
|
for (const key of Object.keys(file)) {
|
|
28
|
-
if (!
|
|
29
|
-
throw new Error(`${path}: unknown key "${key}" (valid keys: ${
|
|
28
|
+
if (!FILE_KEYS.includes(key)) {
|
|
29
|
+
throw new Error(`${path}: unknown key "${key}" (valid keys: ${FILE_KEYS.join(", ")})`);
|
|
30
|
+
}
|
|
30
31
|
}
|
|
31
32
|
if (file.$schema !== undefined && typeof file.$schema !== "string") {
|
|
32
33
|
throw new Error(`${path}: "$schema" must be a string`);
|
|
33
34
|
}
|
|
35
|
+
if (file.mcpServers !== undefined &&
|
|
36
|
+
(typeof file.mcpServers !== "object" || file.mcpServers === null || Array.isArray(file.mcpServers))) {
|
|
37
|
+
throw new Error(`${path}: "mcpServers" must be an object of servers by name`);
|
|
38
|
+
}
|
|
34
39
|
return file;
|
|
35
40
|
}
|
|
36
41
|
/** One part of `context.json` at `path`, declared by `declare`; its refusal names the file. */
|
|
@@ -45,24 +50,19 @@ function declarePart(path, declare) {
|
|
|
45
50
|
/** Read `text`, the contents of `path`, whole: what an edit, which writes the whole, reads. */
|
|
46
51
|
export function parseContextFile(text, path) {
|
|
47
52
|
const file = parseFile(text, path);
|
|
48
|
-
const declared = declarePart(path, () => declareContent(file.content));
|
|
53
|
+
const declared = declarePart(path, () => declareContent(file.content, { origin: "program", file: CONTEXT_FILE }));
|
|
49
54
|
const environment = declarePart(path, () => declareEnvironment(file.environment));
|
|
50
55
|
return {
|
|
51
56
|
...(file.$schema !== undefined ? { $schema: file.$schema } : {}),
|
|
52
57
|
content: (file.content ?? {}),
|
|
53
58
|
declared,
|
|
54
59
|
...(environment !== undefined ? { environment } : {}),
|
|
60
|
+
...(file.mcpServers !== undefined ? { mcpServers: file.mcpServers } : {}),
|
|
55
61
|
};
|
|
56
62
|
}
|
|
57
|
-
/**
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const bound = declarePart(path, () => declareBindings(file.content, declared, resolve(agentDir)));
|
|
61
|
-
return { content: (file.content ?? {}), bound };
|
|
62
|
-
}
|
|
63
|
-
/** The agent's context file `name` and its path, or undefined text when it has none (it declares nothing). */
|
|
64
|
-
function contextFileAt(agentDir, name = CONTEXT_FILE) {
|
|
65
|
-
const path = join(resolve(agentDir), name);
|
|
63
|
+
/** The agent's `context.json` and its path, or undefined text when it has none (it declares nothing). */
|
|
64
|
+
function contextFileAt(agentDir) {
|
|
65
|
+
const path = join(resolve(agentDir), CONTEXT_FILE);
|
|
66
66
|
try {
|
|
67
67
|
return { path, text: readFileSync(path, "utf8") };
|
|
68
68
|
}
|
|
@@ -77,16 +77,25 @@ export function readContextFile(agentDir) {
|
|
|
77
77
|
const { path, text } = contextFileAt(agentDir);
|
|
78
78
|
return text === undefined ? { content: {}, declared: [] } : parseContextFile(text, path);
|
|
79
79
|
}
|
|
80
|
-
/**
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
/** The content of the agent in `agentDir`: what `context.json` declares, and what this machine binds. */
|
|
86
|
-
export function loadContent(agentDir) {
|
|
80
|
+
/**
|
|
81
|
+
* The content of the agent whose program is in `agentDir`: what its `context.json` declares, and what `instance` adds
|
|
82
|
+
* to it (an instance only adds: a name both declare is refused).
|
|
83
|
+
*/
|
|
84
|
+
export function loadContent(agentDir, instance) {
|
|
87
85
|
const { path, text } = contextFileAt(agentDir);
|
|
88
|
-
const
|
|
89
|
-
|
|
86
|
+
const program = text === undefined
|
|
87
|
+
? []
|
|
88
|
+
: declarePart(path, () => declareContent(parseFile(text, path).content, { origin: "program", file: CONTEXT_FILE }));
|
|
89
|
+
return joinContent(program, instance === undefined ? [] : instanceContent(instance));
|
|
90
|
+
}
|
|
91
|
+
/** What an instance configuration's `content` declares. */
|
|
92
|
+
export function instanceContent({ values, label, directory }) {
|
|
93
|
+
const content = typeof values === "object" && values !== null ? values.content : undefined;
|
|
94
|
+
return declarePart(label, () => declareContent(content, {
|
|
95
|
+
origin: "instance",
|
|
96
|
+
file: label,
|
|
97
|
+
...(directory === undefined ? {} : { base: directory }),
|
|
98
|
+
}));
|
|
90
99
|
}
|
|
91
100
|
/** The environment the agent in `agentDir` declares, or undefined when it declares none. */
|
|
92
101
|
export function loadEnvironment(agentDir) {
|
|
@@ -96,6 +105,11 @@ export function loadEnvironment(agentDir) {
|
|
|
96
105
|
const file = parseFile(text, path);
|
|
97
106
|
return declarePart(path, () => declareEnvironment(file.environment));
|
|
98
107
|
}
|
|
108
|
+
/** The program's `mcpServers` map as written in `context.json`, and the file's path, which names it in a refusal. */
|
|
109
|
+
export function readProgramMcpServers(agentDir) {
|
|
110
|
+
const { path, text } = contextFileAt(agentDir);
|
|
111
|
+
return { values: text === undefined ? undefined : parseFile(text, path).mcpServers, path };
|
|
112
|
+
}
|
|
99
113
|
/** The text `context.json` is written as. An environment that declares nothing is left out. */
|
|
100
114
|
export function contextFileText(file) {
|
|
101
115
|
const packages = file.environment?.packages ?? {};
|
|
@@ -110,10 +124,9 @@ export function contextFileText(file) {
|
|
|
110
124
|
...(file.$schema !== undefined ? { $schema: file.$schema } : {}),
|
|
111
125
|
content: file.content,
|
|
112
126
|
...(Object.keys(environment).length > 0 ? { environment } : {}),
|
|
127
|
+
...(file.mcpServers !== undefined && Object.keys(file.mcpServers).length > 0
|
|
128
|
+
? { mcpServers: file.mcpServers }
|
|
129
|
+
: {}),
|
|
113
130
|
};
|
|
114
131
|
return `${JSON.stringify(out, null, 2)}\n`;
|
|
115
132
|
}
|
|
116
|
-
/** The text `context.local.json` is written as. */
|
|
117
|
-
export function localContextFileText(content) {
|
|
118
|
-
return `${JSON.stringify({ content }, null, 2)}\n`;
|
|
119
|
-
}
|
package/dist/content/git.d.ts
CHANGED
|
@@ -8,16 +8,6 @@ export declare function checkoutOf(dir: string): {
|
|
|
8
8
|
root: string;
|
|
9
9
|
origin?: string;
|
|
10
10
|
} | undefined;
|
|
11
|
-
/**
|
|
12
|
-
* Why the directory `local` cannot stand for github `repo` on this machine, or undefined when it can: it is the root
|
|
13
|
-
* of a checkout whose `origin` is that repository.
|
|
14
|
-
*/
|
|
15
|
-
export declare function checkoutProblem(local: string, repo: string): string | undefined;
|
|
16
|
-
/**
|
|
17
|
-
* What to say when the checkout `dir` is not at `ref`, or undefined when it is: on that branch, or at the commit `ref`
|
|
18
|
-
* names. FastAgent never moves a checkout the user owns, so this is said, not acted on.
|
|
19
|
-
*/
|
|
20
|
-
export declare function refNotice(dir: string, ref: string): string | undefined;
|
|
21
11
|
/**
|
|
22
12
|
* What a start did with a clone: made it, moved it to what the remote has now, found it there already, or kept it as
|
|
23
13
|
* it is, for the `reason` given (git refused to touch the agent's work, or the remote could not be reached).
|
package/dist/content/git.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* `ref`, and a clone, made or brought up to date in place. git's own configuration applies throughout (credential helpers, `url.<base>.insteadOf`), so
|
|
4
4
|
* a private repository is reached the way the user's own `git clone` reaches it.
|
|
5
5
|
*/
|
|
6
|
-
import { existsSync, mkdirSync,
|
|
6
|
+
import { existsSync, mkdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
7
7
|
import { basename, dirname, join } from "node:path";
|
|
8
8
|
import { gitFor } from "../git.js";
|
|
9
9
|
const WHY = "github content needs it on this machine";
|
|
10
|
-
const { answer:
|
|
10
|
+
const { answer: checkoutAnswer } = gitFor(WHY);
|
|
11
11
|
/** The `owner/repo` a GitHub remote URL names, in any of its spellings (https, ssh, scp-like), or undefined. */
|
|
12
12
|
export function githubRepoOf(url) {
|
|
13
13
|
const match = /^(?:(?:https?|git|ssh):\/\/(?:[^@/]+@)?github\.com\/|(?:[^@/:]+@)?github\.com:)([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/i.exec(url.trim());
|
|
@@ -24,46 +24,12 @@ function githubUrl(repo) {
|
|
|
24
24
|
export function checkoutOf(dir) {
|
|
25
25
|
if (!existsSync(dir) || !statSync(dir).isDirectory())
|
|
26
26
|
return undefined;
|
|
27
|
-
const root =
|
|
27
|
+
const root = checkoutAnswer(["rev-parse", "--show-toplevel"], dir);
|
|
28
28
|
if (root === undefined)
|
|
29
29
|
return undefined;
|
|
30
|
-
const origin =
|
|
30
|
+
const origin = checkoutAnswer(["config", "--get", "remote.origin.url"], root);
|
|
31
31
|
return { root, ...(origin ? { origin } : {}) };
|
|
32
32
|
}
|
|
33
|
-
/**
|
|
34
|
-
* Why the directory `local` cannot stand for github `repo` on this machine, or undefined when it can: it is the root
|
|
35
|
-
* of a checkout whose `origin` is that repository.
|
|
36
|
-
*/
|
|
37
|
-
export function checkoutProblem(local, repo) {
|
|
38
|
-
if (!existsSync(local))
|
|
39
|
-
return "does not exist";
|
|
40
|
-
const checkout = checkoutOf(local);
|
|
41
|
-
if (checkout === undefined)
|
|
42
|
-
return "is not a git checkout";
|
|
43
|
-
if (realpathSync(checkout.root) !== realpathSync(local))
|
|
44
|
-
return `is inside the checkout ${checkout.root}, not its root`;
|
|
45
|
-
const of = checkout.origin === undefined ? undefined : githubRepoOf(checkout.origin);
|
|
46
|
-
if (of?.toLowerCase() !== repo.toLowerCase()) {
|
|
47
|
-
return `is a checkout of ${checkout.origin ?? "no origin"}, not of github ${repo}`;
|
|
48
|
-
}
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* What to say when the checkout `dir` is not at `ref`, or undefined when it is: on that branch, or at the commit `ref`
|
|
53
|
-
* names. FastAgent never moves a checkout the user owns, so this is said, not acted on.
|
|
54
|
-
*/
|
|
55
|
-
export function refNotice(dir, ref) {
|
|
56
|
-
const branch = gitAnswer(["symbolic-ref", "--quiet", "--short", "HEAD"], dir);
|
|
57
|
-
if (branch === ref)
|
|
58
|
-
return undefined;
|
|
59
|
-
const head = gitAnswer(["rev-parse", "HEAD"], dir);
|
|
60
|
-
const target = gitAnswer(["rev-parse", "--verify", "--quiet", "--end-of-options", `${ref}^{commit}`], dir);
|
|
61
|
-
if (head !== undefined && head === target) {
|
|
62
|
-
return undefined;
|
|
63
|
-
}
|
|
64
|
-
const at = branch ?? `a detached ${head?.slice(0, 7) ?? "HEAD"}`;
|
|
65
|
-
return `the checkout is on ${at}, declared ${ref}; it is left as it is`;
|
|
66
|
-
}
|
|
67
33
|
/** A commit named in full: fetched by its hash, since `clone --branch` takes only a branch or a tag. */
|
|
68
34
|
const FULL_COMMIT = /^[0-9a-f]{40}$/i;
|
|
69
35
|
/**
|
|
@@ -97,7 +63,7 @@ const HELPER_KEY = "credential.https://github.com.helper";
|
|
|
97
63
|
*/
|
|
98
64
|
export async function refreshClone(repo, ref, dir, env = process.env) {
|
|
99
65
|
// The agent's environment, whose GITHUB_TOKEN the clone's credential helper reads.
|
|
100
|
-
const { run: runGit } = gitFor(WHY, env);
|
|
66
|
+
const { run: runGit, answer: gitAnswer } = gitFor(WHY, env);
|
|
101
67
|
// A library caller can hand this anything; git reads a leading "-" as an option, and no repository or ref has one
|
|
102
68
|
// (a declaration with one is refused at load, declare.ts).
|
|
103
69
|
if (repo.startsWith("-") || ref?.startsWith("-")) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type DeclaredContent } from "./declare.ts";
|
|
2
2
|
import { type CloneOutcome } from "./git.ts";
|
|
3
3
|
/** A content entry, resolved for this instance. */
|
|
4
4
|
export type ResolvedContent = {
|
|
@@ -8,42 +8,38 @@ export type ResolvedContent = {
|
|
|
8
8
|
description?: string;
|
|
9
9
|
/** Where the agent reaches it on this instance, absolute. */
|
|
10
10
|
location: string;
|
|
11
|
-
/** What to tell the user about how it resolved: a
|
|
11
|
+
/** What to tell the user about how it resolved: a clone not made yet. */
|
|
12
12
|
notices: string[];
|
|
13
13
|
} & ({
|
|
14
|
-
/** A directory
|
|
14
|
+
/** A directory of this machine, where the instance configuration says. */
|
|
15
15
|
kind: "local";
|
|
16
16
|
} | {
|
|
17
|
+
/** A clone fastagent makes at `content/<name>` and brings up to date at each start while it holds nothing of the agent's. */
|
|
17
18
|
kind: "github";
|
|
18
19
|
/** `owner/repo`. */
|
|
19
20
|
repo: string;
|
|
20
21
|
ref?: string;
|
|
21
|
-
/**
|
|
22
|
-
* A clone fastagent makes at `content/<name>` and brings up to date at each start while it holds nothing of the
|
|
23
|
-
* agent's. False when this machine binds its own checkout, which is never touched.
|
|
24
|
-
*/
|
|
25
|
-
clone: boolean;
|
|
26
22
|
});
|
|
23
|
+
/** The directories an instance's content is kept apart from. */
|
|
24
|
+
export interface ContentPlaces {
|
|
25
|
+
/** The definition's own directory, which is released and read as it is. */
|
|
26
|
+
dir: string;
|
|
27
|
+
/** The instance's working directory, where clones are made. */
|
|
28
|
+
workdir: string;
|
|
29
|
+
}
|
|
27
30
|
/** Resolve the agent's content for this instance. Refuses, naming the entry, what cannot be. */
|
|
28
|
-
export declare function resolveContent(
|
|
31
|
+
export declare function resolveContent(declared: readonly DeclaredContent[], places: ContentPlaces): ResolvedContent[];
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* Whether the content `name` can be the directory `target` of this machine: one that exists, and is apart from the
|
|
34
|
+
* definition's directory, since a definition is released, content is kept, and one directory cannot be both. The
|
|
35
|
+
* working directory is the instance's own writable place, so a directory inside it is only a directory the agent
|
|
36
|
+
* already reaches. Refuses, naming the entry, what cannot be.
|
|
33
37
|
*/
|
|
34
|
-
export declare function
|
|
35
|
-
/** What is said of a binding {@link contentAbsentHere} returns, after the word "content". */
|
|
36
|
-
export declare function absentNotice(binding: BoundContent): string;
|
|
37
|
-
/**
|
|
38
|
-
* Whether the content `name` can be bound to the directory `target` of this machine: a directory, apart from the agent
|
|
39
|
-
* directory, and for a declared repository the root of a checkout of it. Returns what to tell the user (a checkout off
|
|
40
|
-
* its `ref`); refuses, naming the entry, what cannot be.
|
|
41
|
-
*/
|
|
42
|
-
export declare function checkBinding(agentDir: string, name: string, target: string, repository?: Pick<DeclaredContent, "repo" | "ref">): string[];
|
|
38
|
+
export declare function checkDirectory({ dir }: Pick<ContentPlaces, "dir">, name: string, target: string): void;
|
|
43
39
|
/**
|
|
44
40
|
* Make a clone real, or bring it up to date at its `ref` in place, by git's rules, which never overwrite what the
|
|
45
41
|
* agent did in it (git.ts `refreshClone`): what git refuses, or a remote that cannot be reached, keeps it as it is,
|
|
46
|
-
* with the reason. Only a process that runs the agent calls this.
|
|
42
|
+
* with the reason. Only a process that runs the agent calls this.
|
|
47
43
|
*/
|
|
48
44
|
export declare function cloneContent(entry: Extract<ResolvedContent, {
|
|
49
45
|
kind: "github";
|
package/dist/content/resolve.js
CHANGED
|
@@ -1,83 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WHERE each content entry is, for THIS instance: the one answer the prompt, the coding tools, authored tools, `info`
|
|
3
|
-
* and `deploy` all read (docs/design/core.md §2). A repository
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* and `deploy` all read (docs/design/core.md §2). A repository is fastagent's clone at `content/<name>` in the
|
|
4
|
+
* instance's working directory; a directory is where the instance configuration says. Reads the disk (git included);
|
|
5
|
+
* never the network, never a write. Making a clone real is `cloneContent`'s, and only a process that runs the agent
|
|
6
|
+
* asks for it.
|
|
7
7
|
*/
|
|
8
8
|
import { lstatSync, realpathSync, statSync } from "node:fs";
|
|
9
9
|
import { basename, dirname, join } from "node:path";
|
|
10
|
-
import {
|
|
10
|
+
import { contentEntryPath } from "../paths.js";
|
|
11
11
|
import { nestingError } from "./declare.js";
|
|
12
|
-
import {
|
|
12
|
+
import { refreshClone } from "./git.js";
|
|
13
13
|
import { ensureContentDir } from "./mount.js";
|
|
14
14
|
/** Resolve the agent's content for this instance. Refuses, naming the entry, what cannot be. */
|
|
15
|
-
export function resolveContent(
|
|
16
|
-
|
|
17
|
-
const local = bound
|
|
18
|
-
.filter((binding) => binding.kind === "local" && statOrMissing(binding.path) !== undefined)
|
|
19
|
-
.map((binding) => resolveLocal(agentDir, binding));
|
|
20
|
-
return [...shared, ...local];
|
|
15
|
+
export function resolveContent(declared, places) {
|
|
16
|
+
return declared.map((entry) => entry.kind === "github" ? resolveRepository(places, entry) : resolveLocal(places, entry));
|
|
21
17
|
}
|
|
22
|
-
|
|
23
|
-
* The bindings of a directory only this machine has that point at nothing here: the directory was moved or removed.
|
|
24
|
-
* Not an error, since the binding is this machine's alone; the agent is not told of it, and whoever opens the agent
|
|
25
|
-
* says so.
|
|
26
|
-
*/
|
|
27
|
-
export function contentAbsentHere({ bound }) {
|
|
28
|
-
return bound.filter((binding) => binding.kind === "local" && statOrMissing(binding.path) === undefined);
|
|
29
|
-
}
|
|
30
|
-
/** What is said of a binding {@link contentAbsentHere} returns, after the word "content". */
|
|
31
|
-
export function absentNotice(binding) {
|
|
32
|
-
return (`${binding.name}: bound in ${LOCAL_CONTEXT_FILE} to ${binding.path}, which does not exist here: the agent is ` +
|
|
33
|
-
`not told of it`);
|
|
34
|
-
}
|
|
35
|
-
function resolveRepository(agentDir, entry, checkout) {
|
|
18
|
+
function resolveRepository({ workdir }, entry) {
|
|
36
19
|
const { name, description } = entry;
|
|
37
|
-
const
|
|
38
|
-
name,
|
|
39
|
-
...(description !== undefined ? { description } : {}),
|
|
40
|
-
kind: "github",
|
|
41
|
-
repo: entry.repo,
|
|
42
|
-
...(entry.ref !== undefined ? { ref: entry.ref } : {}),
|
|
43
|
-
};
|
|
44
|
-
if (checkout !== undefined && statOrMissing(checkout.path) !== undefined) {
|
|
45
|
-
const location = realpathSync(checkout.path);
|
|
46
|
-
const notices = checkBinding(agentDir, name, location, entry);
|
|
47
|
-
return { ...repository, location, notices, clone: false };
|
|
48
|
-
}
|
|
49
|
-
const location = contentEntryPath(agentDir, name);
|
|
20
|
+
const location = contentEntryPath(workdir, name);
|
|
50
21
|
const found = lstatOrMissing(location);
|
|
51
22
|
if (found?.isSymbolicLink()) {
|
|
52
23
|
throw new Error(`content "${name}": content/${name} is a link, and content/ holds only the clones fastagent makes — remove ` +
|
|
53
|
-
`the link, and
|
|
24
|
+
`the link, and add the directory it points at as content of its own (fastagent content add <dir>)`);
|
|
54
25
|
}
|
|
55
26
|
if (found && !found.isDirectory())
|
|
56
27
|
throw new Error(`content "${name}": ${location} is not a directory`);
|
|
57
|
-
const notices = found ? [] : ["not cloned yet: it is cloned when the agent starts"];
|
|
58
|
-
if (checkout !== undefined) {
|
|
59
|
-
notices.unshift(`bound in ${LOCAL_CONTEXT_FILE} to ${checkout.path}, which does not exist here: the clone is used`);
|
|
60
|
-
}
|
|
61
|
-
return { ...repository, location, notices, clone: true };
|
|
62
|
-
}
|
|
63
|
-
function resolveLocal(agentDir, binding) {
|
|
64
|
-
const { name, description } = binding;
|
|
65
|
-
const location = realpathSync(binding.path);
|
|
66
|
-
checkBinding(agentDir, name, location);
|
|
67
28
|
return {
|
|
68
29
|
name,
|
|
69
30
|
...(description !== undefined ? { description } : {}),
|
|
31
|
+
kind: "github",
|
|
32
|
+
repo: entry.repo,
|
|
33
|
+
...(entry.ref !== undefined ? { ref: entry.ref } : {}),
|
|
70
34
|
location,
|
|
71
|
-
notices: [],
|
|
35
|
+
notices: found ? [] : ["not cloned yet: it is cloned when the agent starts"],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function resolveLocal(places, entry) {
|
|
39
|
+
const { name, description } = entry;
|
|
40
|
+
checkDirectory(places, name, entry.path);
|
|
41
|
+
return {
|
|
42
|
+
name,
|
|
43
|
+
...(description !== undefined ? { description } : {}),
|
|
72
44
|
kind: "local",
|
|
45
|
+
location: realpathSync(entry.path),
|
|
46
|
+
notices: [],
|
|
73
47
|
};
|
|
74
48
|
}
|
|
75
49
|
/**
|
|
76
|
-
* Whether the content `name` can be
|
|
77
|
-
* directory,
|
|
78
|
-
*
|
|
50
|
+
* Whether the content `name` can be the directory `target` of this machine: one that exists, and is apart from the
|
|
51
|
+
* definition's directory, since a definition is released, content is kept, and one directory cannot be both. The
|
|
52
|
+
* working directory is the instance's own writable place, so a directory inside it is only a directory the agent
|
|
53
|
+
* already reaches. Refuses, naming the entry, what cannot be.
|
|
79
54
|
*/
|
|
80
|
-
export function
|
|
55
|
+
export function checkDirectory({ dir }, name, target) {
|
|
81
56
|
const stat = statOrMissing(target);
|
|
82
57
|
if (!stat)
|
|
83
58
|
throw new Error(`content "${name}": ${target} does not exist`);
|
|
@@ -85,25 +60,16 @@ export function checkBinding(agentDir, name, target, repository) {
|
|
|
85
60
|
throw new Error(`content "${name}": ${target} is not a directory`);
|
|
86
61
|
// A symlink can still put one inside the other, so ask of the real paths — including for an agent directory `init`
|
|
87
62
|
// is about to create, which a symlinked ancestor still places.
|
|
88
|
-
const nested = nestingError(realPathOf(
|
|
63
|
+
const nested = nestingError(realPathOf(dir), realpathSync(target), name);
|
|
89
64
|
if (nested)
|
|
90
65
|
throw new Error(nested);
|
|
91
|
-
if (repository === undefined)
|
|
92
|
-
return [];
|
|
93
|
-
const problem = checkoutProblem(target, repository.repo);
|
|
94
|
-
if (problem)
|
|
95
|
-
throw new Error(`content "${name}": ${target} ${problem}`);
|
|
96
|
-
const offRef = repository.ref === undefined ? undefined : refNotice(target, repository.ref);
|
|
97
|
-
return offRef ? [offRef] : [];
|
|
98
66
|
}
|
|
99
67
|
/**
|
|
100
68
|
* Make a clone real, or bring it up to date at its `ref` in place, by git's rules, which never overwrite what the
|
|
101
69
|
* agent did in it (git.ts `refreshClone`): what git refuses, or a remote that cannot be reached, keeps it as it is,
|
|
102
|
-
* with the reason. Only a process that runs the agent calls this.
|
|
70
|
+
* with the reason. Only a process that runs the agent calls this.
|
|
103
71
|
*/
|
|
104
72
|
export async function cloneContent(entry, env = process.env) {
|
|
105
|
-
if (!entry.clone)
|
|
106
|
-
throw new Error(`content "${entry.name}" is bound to the checkout ${entry.location}, not a clone`);
|
|
107
73
|
ensureContentDir(dirname(entry.location));
|
|
108
74
|
return refreshClone(entry.repo, entry.ref, entry.location, env).catch((error) => {
|
|
109
75
|
throw new Error(`content "${entry.name}": ${error.message}`);
|
package/dist/content/source.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ContentEntry } from "./declare.ts";
|
|
2
2
|
export interface SourceOptions {
|
|
3
3
|
name?: string;
|
|
4
4
|
/** A github entry's branch, tag or commit. */
|
|
@@ -6,19 +6,18 @@ export interface SourceOptions {
|
|
|
6
6
|
description?: string;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* A content entry to add, by name
|
|
10
|
-
*
|
|
9
|
+
* A content entry to add, by name. It is written to the instance's `fastagent.json`, or, with `program`, to the
|
|
10
|
+
* program's `context.json`, which every instance of it reaches and so holds repositories only.
|
|
11
11
|
*/
|
|
12
12
|
export interface ContentAddition {
|
|
13
13
|
name: string;
|
|
14
|
-
entry
|
|
15
|
-
|
|
14
|
+
entry: ContentEntry;
|
|
15
|
+
program?: boolean;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* The addition `source` asks for, with what to tell the user about how it was read. `github:owner/repo` is a
|
|
19
|
-
* repository fastagent clones.
|
|
20
|
-
*
|
|
21
|
-
* bound on this machine alone. The name defaults to the repository's or the directory's.
|
|
19
|
+
* repository fastagent clones. Any other source is a directory the agent works in as it is, a checkout of a GitHub
|
|
20
|
+
* repository included, which is then said. The name defaults to the repository's or the directory's.
|
|
22
21
|
*/
|
|
23
22
|
export declare function readContentSource(source: string, cwd: string, options?: SourceOptions): {
|
|
24
23
|
addition: ContentAddition;
|
package/dist/content/source.js
CHANGED
|
@@ -1,52 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* What a command's `<source>` adds (`init --content`, `fastagent content add`; docs/design/agent-cli.md §3), read the
|
|
3
|
-
* same way by both: `github:owner/repo`,
|
|
3
|
+
* same way by both: `github:owner/repo`, or a directory.
|
|
4
4
|
*/
|
|
5
|
-
import { realpathSync } from "node:fs";
|
|
6
5
|
import { basename, resolve } from "node:path";
|
|
7
6
|
import { isGithubRepo } from "./declare.js";
|
|
8
7
|
import { checkoutOf, githubRepoOf } from "./git.js";
|
|
9
8
|
/**
|
|
10
9
|
* The addition `source` asks for, with what to tell the user about how it was read. `github:owner/repo` is a
|
|
11
|
-
* repository fastagent clones.
|
|
12
|
-
*
|
|
13
|
-
* bound on this machine alone. The name defaults to the repository's or the directory's.
|
|
10
|
+
* repository fastagent clones. Any other source is a directory the agent works in as it is, a checkout of a GitHub
|
|
11
|
+
* repository included, which is then said. The name defaults to the repository's or the directory's.
|
|
14
12
|
*/
|
|
15
13
|
export function readContentSource(source, cwd, options = {}) {
|
|
16
|
-
const treated = {
|
|
17
|
-
...(options.description !== undefined ? { description: options.description } : {}),
|
|
18
|
-
};
|
|
19
|
-
const ref = options.ref !== undefined ? { ref: options.ref } : {};
|
|
14
|
+
const treated = options.description !== undefined ? { description: options.description } : {};
|
|
20
15
|
const repoName = (repo) => repo.slice(repo.indexOf("/") + 1);
|
|
21
16
|
if (source.startsWith("github:")) {
|
|
22
17
|
const repo = source.slice("github:".length);
|
|
23
18
|
if (!isGithubRepo(repo))
|
|
24
19
|
throw new Error(`${source} names no repository: write github:owner/repo`);
|
|
20
|
+
const ref = options.ref !== undefined ? { ref: options.ref } : {};
|
|
25
21
|
return {
|
|
26
22
|
addition: { name: options.name ?? repoName(repo), entry: { github: repo, ...ref, ...treated } },
|
|
27
23
|
notes: [],
|
|
28
24
|
};
|
|
29
25
|
}
|
|
26
|
+
if (options.ref !== undefined) {
|
|
27
|
+
throw new Error(`--ref applies to a repository, and ${resolve(cwd, source)} is a directory`);
|
|
28
|
+
}
|
|
30
29
|
const dir = resolve(cwd, source);
|
|
31
30
|
const checkout = checkoutOf(dir);
|
|
32
31
|
const repo = checkout?.origin === undefined ? undefined : githubRepoOf(checkout.origin);
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
const root = checkout.root;
|
|
36
|
-
return {
|
|
37
|
-
addition: {
|
|
38
|
-
name: options.name ?? repoName(repo),
|
|
39
|
-
entry: { github: repo, ...ref, ...treated },
|
|
40
|
-
binding: { path: root },
|
|
41
|
-
},
|
|
42
|
-
notes: [],
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
if (options.ref !== undefined) {
|
|
46
|
-
throw new Error(`--ref applies to a repository, and ${dir} is bound as a directory of this machine`);
|
|
47
|
-
}
|
|
48
|
-
const notes = repo !== undefined && !atRoot
|
|
49
|
-
? [`${dir} is in a checkout of github ${repo}: add github:${repo} for the whole repository`]
|
|
50
|
-
: [];
|
|
51
|
-
return { addition: { name: options.name ?? basename(dir), binding: { path: dir, ...treated } }, notes };
|
|
32
|
+
const notes = repo === undefined ? [] : [`${dir} is in a checkout of github ${repo}: add github:${repo} to have it cloned`];
|
|
33
|
+
return { addition: { name: options.name ?? basename(dir), entry: { path: dir, ...treated } }, notes };
|
|
52
34
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SetupGuide } from "./channels/setup.ts";
|
|
2
|
+
/** What the person pastes at one step: its fields are checked together. */
|
|
3
|
+
export interface CredentialsStep {
|
|
4
|
+
label: string;
|
|
5
|
+
guide: SetupGuide;
|
|
6
|
+
fields: readonly {
|
|
7
|
+
name: string;
|
|
8
|
+
label: string;
|
|
9
|
+
secret?: boolean;
|
|
10
|
+
}[];
|
|
11
|
+
}
|
|
12
|
+
export interface CredentialsPage {
|
|
13
|
+
url: string;
|
|
14
|
+
/** Settles once every value was accepted; rejects when `accept` failed for a reason other than a refusal. */
|
|
15
|
+
done: Promise<void>;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export declare function startCredentialsPage(input: {
|
|
19
|
+
/** "Connect Slack". */
|
|
20
|
+
title: string;
|
|
21
|
+
/** What the done page says: "Slack connected". */
|
|
22
|
+
doneHeading: string;
|
|
23
|
+
steps: readonly CredentialsStep[];
|
|
24
|
+
/** Check a step's values and keep them. Throws {@link SetupRefused} to stay on the form with the reason; anything else ends the page. */
|
|
25
|
+
accept(step: number, values: Record<string, string>): Promise<void>;
|
|
26
|
+
}): Promise<CredentialsPage>;
|