Skip to content

Commit e14fd4d

Browse files
committed
Add setup and discovery commands
1 parent 92ddd77 commit e14fd4d

3 files changed

Lines changed: 150 additions & 42 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Human/debug command-line interface for AgentDispatch.
44

55
```bash
6+
agentdispatch init --region us-west-2 --runtime-arn arn:aws:bedrock-agentcore:...
7+
agentdispatch providers
8+
agentdispatch capabilities --provider aws
9+
agentdispatch accounts
610
agentdispatch run --provider aws --account-profile dev-aws --capability agent-runtime --task-type agent.run --instruction "Run a long task"
711
agentdispatch status task_...
812
agentdispatch logs task_...

src/index.ts

Lines changed: 113 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,94 @@
11
#!/usr/bin/env node
2-
import { readFile } from "node:fs/promises";
2+
import { readFile, writeFile } from "node:fs/promises";
3+
import { fileURLToPath } from "node:url";
34
import { Command } from "commander";
45
import { RuntimeService, type AgentDispatchConfig } from "@agentdispatch/core";
56
import { AgentDispatchClient } from "@agentdispatch/sdk";
67
import { SqliteTaskStore } from "@agentdispatch/store-sqlite";
78
import { AwsAgentCoreAdapter } from "@agentdispatch/adapter-aws-agentcore";
89

9-
const program = new Command();
10-
11-
program.name("agentdispatch").description("Provider-neutral agent task dispatcher").version("0.1.0");
12-
13-
program
14-
.command("run")
15-
.requiredOption("--provider <provider>")
16-
.requiredOption("--account-profile <name>")
17-
.requiredOption("--capability <capability>")
18-
.requiredOption("--task-type <type>")
19-
.option("--target-mode <mode>", "Target mode", "session")
20-
.option("--instruction <text>")
21-
.option("--command <command>")
22-
.option("--config <path>", "Config file", "agentdispatch.config.json")
23-
.action(async (options) => {
24-
const client = await createClient(options.config);
25-
const handle = await client.dispatchTask({
26-
provider: options.provider,
27-
accountProfile: options.accountProfile,
28-
capability: options.capability,
29-
taskType: options.taskType,
30-
target: { mode: options.targetMode },
31-
input: { instruction: options.instruction, command: options.command }
10+
export function buildProgram(output: Pick<Console, "log" | "error"> = console): Command {
11+
const program = new Command();
12+
13+
program.name("agentdispatch").description("Provider-neutral agent task dispatcher").version("0.1.0");
14+
15+
program
16+
.command("init")
17+
.option("--config <path>", "Config file", "agentdispatch.config.json")
18+
.option("--runtime-arn <arn>", "Existing AWS AgentCore runtime ARN", "arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/example")
19+
.option("--region <region>", "AWS region", "us-west-2")
20+
.action(async (options) => {
21+
const config = sampleConfig(options.region, options.runtimeArn);
22+
await writeFile(options.config, `${JSON.stringify(config, null, 2)}\n`);
23+
output.log(`Wrote ${options.config}`);
3224
});
33-
console.log(JSON.stringify(handle, null, 2));
34-
});
3525

36-
program.command("status").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
37-
console.log(JSON.stringify(await (await createClient(options.config)).getTaskStatus(taskId), null, 2));
38-
});
26+
program
27+
.command("providers")
28+
.option("--config <path>", "Config file", "agentdispatch.config.json")
29+
.action(async (options) => {
30+
output.log(JSON.stringify((await createClient(options.config)).listProviders(), null, 2));
31+
});
3932

40-
program.command("logs").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
41-
const logs = await (await createClient(options.config)).getTaskLogs(taskId);
42-
process.stdout.write(logs.data);
43-
});
33+
program
34+
.command("capabilities")
35+
.option("--provider <provider>")
36+
.option("--config <path>", "Config file", "agentdispatch.config.json")
37+
.action(async (options) => {
38+
output.log(JSON.stringify((await createClient(options.config)).listCapabilities(options.provider), null, 2));
39+
});
4440

45-
program.command("result").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
46-
console.log(JSON.stringify(await (await createClient(options.config)).getTaskResult(taskId), null, 2));
47-
});
41+
program
42+
.command("accounts")
43+
.option("--config <path>", "Config file", "agentdispatch.config.json")
44+
.action(async (options) => {
45+
output.log(JSON.stringify((await createClient(options.config)).listAccountProfiles(), null, 2));
46+
});
4847

49-
program.command("cancel").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
50-
console.log(JSON.stringify(await (await createClient(options.config)).cancelTask(taskId), null, 2));
51-
});
48+
program
49+
.command("run")
50+
.requiredOption("--provider <provider>")
51+
.requiredOption("--account-profile <name>")
52+
.requiredOption("--capability <capability>")
53+
.requiredOption("--task-type <type>")
54+
.option("--target-mode <mode>", "Target mode", "session")
55+
.option("--instruction <text>")
56+
.option("--command <command>")
57+
.option("--config <path>", "Config file", "agentdispatch.config.json")
58+
.action(async (options) => {
59+
const client = await createClient(options.config);
60+
const handle = await client.dispatchTask({
61+
provider: options.provider,
62+
accountProfile: options.accountProfile,
63+
capability: options.capability,
64+
taskType: options.taskType,
65+
target: { mode: options.targetMode },
66+
input: { instruction: options.instruction, command: options.command }
67+
});
68+
output.log(JSON.stringify(handle, null, 2));
69+
});
5270

53-
void program.parseAsync();
71+
program.command("status").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
72+
output.log(JSON.stringify(await (await createClient(options.config)).getTaskStatus(taskId), null, 2));
73+
});
74+
75+
program.command("logs").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
76+
const logs = await (await createClient(options.config)).getTaskLogs(taskId);
77+
output.log(logs.data);
78+
});
5479

55-
async function createClient(configPath: string): Promise<AgentDispatchClient> {
80+
program.command("result").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
81+
output.log(JSON.stringify(await (await createClient(options.config)).getTaskResult(taskId), null, 2));
82+
});
83+
84+
program.command("cancel").argument("<taskId>").option("--config <path>", "Config file", "agentdispatch.config.json").action(async (taskId, options) => {
85+
output.log(JSON.stringify(await (await createClient(options.config)).cancelTask(taskId), null, 2));
86+
});
87+
88+
return program;
89+
}
90+
91+
export async function createClient(configPath: string): Promise<AgentDispatchClient> {
5692
const config = await loadConfig(configPath);
5793
const stateDir = config.stateDir ?? ".agentdispatch";
5894
const store = new SqliteTaskStore({ stateDir });
@@ -73,7 +109,42 @@ async function createClient(configPath: string): Promise<AgentDispatchClient> {
73109
return new AgentDispatchClient(new RuntimeService({ config, store, adapters }));
74110
}
75111

76-
async function loadConfig(path: string): Promise<AgentDispatchConfig> {
112+
export async function loadConfig(path: string): Promise<AgentDispatchConfig> {
77113
const raw = await readFile(path, "utf8");
78114
return JSON.parse(raw) as AgentDispatchConfig;
79115
}
116+
117+
export function sampleConfig(region: string, runtimeArn: string): AgentDispatchConfig {
118+
return {
119+
stateDir: ".agentdispatch",
120+
accounts: {
121+
"dev-aws": {
122+
provider: "aws",
123+
region,
124+
credentialSource: "aws-sdk-default"
125+
}
126+
},
127+
backends: {
128+
"aws-agentcore": {
129+
provider: "aws",
130+
capability: "agent-runtime",
131+
adapter: "aws-agentcore",
132+
account: "dev-aws",
133+
details: {
134+
runtimeArn,
135+
qualifier: "DEFAULT"
136+
}
137+
}
138+
},
139+
defaults: {
140+
provider: "aws",
141+
accountProfile: "dev-aws",
142+
capability: "agent-runtime",
143+
backend: "aws-agentcore"
144+
}
145+
};
146+
}
147+
148+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
149+
void buildProgram().parseAsync();
150+
}

test/cli.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { mkdtemp, readFile, rm } from "node:fs/promises";
2+
import { join } from "node:path";
3+
import { tmpdir } from "node:os";
4+
import { afterEach, describe, expect, it } from "vitest";
5+
import { buildProgram, sampleConfig } from "../src/index.js";
6+
7+
let stateDir: string | undefined;
8+
9+
afterEach(async () => {
10+
if (stateDir) await rm(stateDir, { recursive: true, force: true });
11+
});
12+
13+
describe("agentdispatch CLI", () => {
14+
it("creates a sample config with init", async () => {
15+
stateDir = await mkdtemp(join(tmpdir(), "agentdispatch-cli-"));
16+
const configPath = join(stateDir, "agentdispatch.config.json");
17+
const output: string[] = [];
18+
const program = buildProgram({ log: (value: string) => output.push(value), error: () => undefined });
19+
20+
await program.parseAsync(["node", "agentdispatch", "init", "--config", configPath, "--region", "us-west-2", "--runtime-arn", "arn:test"]);
21+
const config = JSON.parse(await readFile(configPath, "utf8"));
22+
23+
expect(output[0]).toContain("Wrote");
24+
expect(config.accounts["dev-aws"].provider).toBe("aws");
25+
expect(config.backends["aws-agentcore"].details.runtimeArn).toBe("arn:test");
26+
});
27+
28+
it("builds a provider-neutral sample config", () => {
29+
expect(sampleConfig("us-east-1", "arn:runtime")).toMatchObject({
30+
defaults: { provider: "aws", capability: "agent-runtime" }
31+
});
32+
});
33+
});

0 commit comments

Comments
 (0)