@delega-dev/cli 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +109 -0
- package/dist/api.d.ts +5 -0
- package/dist/api.js +45 -0
- package/dist/commands/agents.d.ts +2 -0
- package/dist/commands/agents.js +86 -0
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.js +66 -0
- package/dist/commands/stats.d.ts +2 -0
- package/dist/commands/stats.js +40 -0
- package/dist/commands/tasks.d.ts +2 -0
- package/dist/commands/tasks.js +153 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.js +32 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +34 -0
- package/dist/ui.d.ts +7 -0
- package/dist/ui.js +79 -0
- package/package.json +42 -0
- package/src/api.ts +61 -0
- package/src/commands/agents.ts +115 -0
- package/src/commands/login.ts +79 -0
- package/src/commands/stats.ts +48 -0
- package/src/commands/tasks.ts +189 -0
- package/src/commands/whoami.ts +47 -0
- package/src/config.ts +53 -0
- package/src/index.ts +41 -0
- package/src/ui.ts +92 -0
- package/tsconfig.json +14 -0
package/dist/ui.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
const BANNER = `
|
|
3
|
+
____ __
|
|
4
|
+
/ __ \\___ / /__ ____ _____ _
|
|
5
|
+
/ / / / _ \\/ / _ \\/ __ \`/ __ \`/
|
|
6
|
+
/ /_/ / __/ / __/ /_/ / /_/ /
|
|
7
|
+
/_____/\\___/_/\\___/\\__, /\\__,_/
|
|
8
|
+
/____/
|
|
9
|
+
Task infrastructure for AI agents
|
|
10
|
+
`;
|
|
11
|
+
export function printBanner() {
|
|
12
|
+
console.log(chalk.cyan(BANNER));
|
|
13
|
+
}
|
|
14
|
+
export function printTable(headers, rows) {
|
|
15
|
+
if (rows.length === 0)
|
|
16
|
+
return;
|
|
17
|
+
const colWidths = headers.map((h, i) => {
|
|
18
|
+
const maxRow = rows.reduce((max, row) => Math.max(max, (row[i] || "").length), 0);
|
|
19
|
+
return Math.max(h.length, maxRow);
|
|
20
|
+
});
|
|
21
|
+
const headerLine = headers
|
|
22
|
+
.map((h, i) => h.padEnd(colWidths[i]))
|
|
23
|
+
.join(" ");
|
|
24
|
+
console.log(chalk.cyan.bold(headerLine));
|
|
25
|
+
const separator = colWidths.map((w) => "─".repeat(w)).join("──");
|
|
26
|
+
console.log(chalk.dim(separator));
|
|
27
|
+
for (const row of rows) {
|
|
28
|
+
const line = row
|
|
29
|
+
.map((cell, i) => (cell || "").padEnd(colWidths[i]))
|
|
30
|
+
.join(" ");
|
|
31
|
+
console.log(line);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function formatDate(iso) {
|
|
35
|
+
if (!iso)
|
|
36
|
+
return "—";
|
|
37
|
+
const d = new Date(iso);
|
|
38
|
+
return d.toLocaleDateString("en-US", {
|
|
39
|
+
month: "short",
|
|
40
|
+
day: "numeric",
|
|
41
|
+
year: "numeric",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export function formatId(id) {
|
|
45
|
+
return id.slice(0, 8);
|
|
46
|
+
}
|
|
47
|
+
export function priorityBadge(n) {
|
|
48
|
+
switch (n) {
|
|
49
|
+
case 1:
|
|
50
|
+
return chalk.red.bold("P1");
|
|
51
|
+
case 2:
|
|
52
|
+
return chalk.yellow.bold("P2");
|
|
53
|
+
case 3:
|
|
54
|
+
return chalk.green("P3");
|
|
55
|
+
case 4:
|
|
56
|
+
return chalk.dim("P4");
|
|
57
|
+
default:
|
|
58
|
+
return chalk.dim(`P${n}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function statusBadge(s) {
|
|
62
|
+
switch (s?.toLowerCase()) {
|
|
63
|
+
case "completed":
|
|
64
|
+
return chalk.green("completed");
|
|
65
|
+
case "in_progress":
|
|
66
|
+
case "in progress":
|
|
67
|
+
return chalk.yellow("in_progress");
|
|
68
|
+
case "pending":
|
|
69
|
+
return chalk.blue("pending");
|
|
70
|
+
case "cancelled":
|
|
71
|
+
case "canceled":
|
|
72
|
+
return chalk.dim("cancelled");
|
|
73
|
+
default:
|
|
74
|
+
return s || "—";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function label(key, value) {
|
|
78
|
+
console.log(`${chalk.cyan.bold(key + ":")} ${value}`);
|
|
79
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@delega-dev/cli",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "CLI for Delega task API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"delega": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "ts-node --esm src/index.ts"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"chalk": "^5.3.0",
|
|
15
|
+
"commander": "^12.1.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.14.0",
|
|
19
|
+
"typescript": "^5.5.0"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Delega <hello@delega.dev>",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/delega-dev/delega-cli"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"delega",
|
|
29
|
+
"ai-agents",
|
|
30
|
+
"task-management",
|
|
31
|
+
"mcp",
|
|
32
|
+
"cli",
|
|
33
|
+
"delegation",
|
|
34
|
+
"agent-infrastructure"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { getApiKey, getApiUrl } from "./config.js";
|
|
2
|
+
|
|
3
|
+
export interface ApiError {
|
|
4
|
+
error?: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function apiCall<T = unknown>(
|
|
9
|
+
method: string,
|
|
10
|
+
path: string,
|
|
11
|
+
body?: unknown,
|
|
12
|
+
): Promise<T> {
|
|
13
|
+
const apiKey = getApiKey();
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
console.error("Not authenticated. Run: delega login");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const url = getApiUrl() + path;
|
|
20
|
+
|
|
21
|
+
const headers: Record<string, string> = {
|
|
22
|
+
"X-Agent-Key": apiKey,
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const options: RequestInit = { method, headers };
|
|
27
|
+
if (body !== undefined) {
|
|
28
|
+
options.body = JSON.stringify(body);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let res: Response;
|
|
32
|
+
try {
|
|
33
|
+
res = await fetch(url, options);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
36
|
+
console.error(`Connection error: ${msg}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (res.status === 401) {
|
|
41
|
+
console.error("Authentication failed. Run: delega login");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let data: unknown;
|
|
46
|
+
const text = await res.text();
|
|
47
|
+
try {
|
|
48
|
+
data = text ? JSON.parse(text) : {};
|
|
49
|
+
} catch {
|
|
50
|
+
data = { message: text };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const errData = data as ApiError;
|
|
55
|
+
const msg = errData.error || errData.message || `Request failed (${res.status})`;
|
|
56
|
+
console.error(`Error: ${msg}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return data as T;
|
|
61
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import node_readline from "node:readline";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { apiCall } from "../api.js";
|
|
5
|
+
import { printTable, formatDate, label } from "../ui.js";
|
|
6
|
+
|
|
7
|
+
interface Agent {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
display_name?: string;
|
|
11
|
+
active?: boolean;
|
|
12
|
+
created_at?: string;
|
|
13
|
+
api_key?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function confirm(question: string): Promise<boolean> {
|
|
17
|
+
const rl = node_readline.createInterface({
|
|
18
|
+
input: process.stdin,
|
|
19
|
+
output: process.stdout,
|
|
20
|
+
});
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
rl.question(question, (answer) => {
|
|
23
|
+
rl.close();
|
|
24
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const agentsList = new Command("list")
|
|
30
|
+
.description("List agents")
|
|
31
|
+
.option("--json", "Output raw JSON")
|
|
32
|
+
.action(async (opts) => {
|
|
33
|
+
const data = await apiCall<Agent[]>("GET", "/v1/agents");
|
|
34
|
+
|
|
35
|
+
if (opts.json) {
|
|
36
|
+
console.log(JSON.stringify(data, null, 2));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const agents = Array.isArray(data) ? data : [data];
|
|
41
|
+
|
|
42
|
+
if (agents.length === 0) {
|
|
43
|
+
console.log("No agents found.");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const headers = ["Name", "Display Name", "Active", "Created"];
|
|
48
|
+
const rows = agents.map((a) => [
|
|
49
|
+
a.name,
|
|
50
|
+
a.display_name || "—",
|
|
51
|
+
a.active !== false ? "yes" : "no",
|
|
52
|
+
formatDate(a.created_at || ""),
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
printTable(headers, rows);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const agentsCreate = new Command("create")
|
|
59
|
+
.description("Create a new agent")
|
|
60
|
+
.argument("<name>", "Agent name")
|
|
61
|
+
.option("--display-name <name>", "Friendly display name")
|
|
62
|
+
.option("--json", "Output raw JSON")
|
|
63
|
+
.action(async (name: string, opts) => {
|
|
64
|
+
const body: Record<string, unknown> = { name };
|
|
65
|
+
if (opts.displayName) body.display_name = opts.displayName;
|
|
66
|
+
|
|
67
|
+
const agent = await apiCall<Agent>("POST", "/v1/agents", body);
|
|
68
|
+
|
|
69
|
+
if (opts.json) {
|
|
70
|
+
console.log(JSON.stringify(agent, null, 2));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log();
|
|
75
|
+
label("Agent Created", agent.name);
|
|
76
|
+
if (agent.display_name) label("Display Name", agent.display_name);
|
|
77
|
+
label("ID", agent.id);
|
|
78
|
+
if (agent.api_key) {
|
|
79
|
+
console.log();
|
|
80
|
+
console.log(` API Key: ${chalk.cyan.bold(agent.api_key)}`);
|
|
81
|
+
console.log(
|
|
82
|
+
chalk.yellow(" Save this key — it will not be shown again."),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
console.log();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const agentsRotate = new Command("rotate")
|
|
89
|
+
.description("Rotate an agent's API key")
|
|
90
|
+
.argument("<id>", "Agent ID")
|
|
91
|
+
.action(async (id: string) => {
|
|
92
|
+
const yes = await confirm(
|
|
93
|
+
`Rotate key for agent ${id}? Old key will stop working immediately. (y/N) `,
|
|
94
|
+
);
|
|
95
|
+
if (!yes) {
|
|
96
|
+
console.log("Cancelled.");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const result = await apiCall<{ api_key: string }>(
|
|
101
|
+
"POST",
|
|
102
|
+
`/v1/agents/${id}/rotate-key`,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
console.log();
|
|
106
|
+
console.log(` New API Key: ${chalk.cyan.bold(result.api_key)}`);
|
|
107
|
+
console.log(chalk.yellow(" Save this key — it will not be shown again."));
|
|
108
|
+
console.log();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export const agentsCommand = new Command("agents")
|
|
112
|
+
.description("Manage agents")
|
|
113
|
+
.addCommand(agentsList)
|
|
114
|
+
.addCommand(agentsCreate)
|
|
115
|
+
.addCommand(agentsRotate);
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import node_readline from "node:readline";
|
|
3
|
+
import { saveConfig, loadConfig } from "../config.js";
|
|
4
|
+
import { printBanner } from "../ui.js";
|
|
5
|
+
|
|
6
|
+
interface Agent {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
display_name?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function prompt(question: string): Promise<string> {
|
|
13
|
+
const rl = node_readline.createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout,
|
|
16
|
+
});
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(question, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
resolve(answer.trim());
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const loginCommand = new Command("login")
|
|
26
|
+
.description("Authenticate with the Delega API")
|
|
27
|
+
.action(async () => {
|
|
28
|
+
printBanner();
|
|
29
|
+
|
|
30
|
+
const key = await prompt("Enter your API key (starts with dlg_): ");
|
|
31
|
+
|
|
32
|
+
if (!key) {
|
|
33
|
+
console.error("No key provided.");
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!key.startsWith("dlg_")) {
|
|
38
|
+
console.error("Invalid key format. Keys start with dlg_");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Validate by calling the API
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
const apiUrl = config.api_url || process.env.DELEGA_API_URL || "https://api.delega.dev";
|
|
45
|
+
|
|
46
|
+
let res: Response;
|
|
47
|
+
try {
|
|
48
|
+
res = await fetch(`${apiUrl}/v1/agents`, {
|
|
49
|
+
headers: {
|
|
50
|
+
"X-Agent-Key": key,
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
} catch (err) {
|
|
55
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
56
|
+
console.error(`Connection error: ${msg}`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
console.error("Invalid API key. Authentication failed.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let agentName = "agent";
|
|
66
|
+
try {
|
|
67
|
+
const data = (await res.json()) as Agent | Agent[];
|
|
68
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
69
|
+
agentName = data[0].display_name || data[0].name;
|
|
70
|
+
} else if (!Array.isArray(data) && data.name) {
|
|
71
|
+
agentName = data.display_name || data.name;
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// Proceed with default name
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
saveConfig({ ...config, api_key: key });
|
|
78
|
+
console.log(`\nLogged in as ${agentName}. Key saved to ~/.delega/config.json`);
|
|
79
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { apiCall } from "../api.js";
|
|
3
|
+
import { label } from "../ui.js";
|
|
4
|
+
|
|
5
|
+
interface Stats {
|
|
6
|
+
total_tasks?: number;
|
|
7
|
+
completed_tasks?: number;
|
|
8
|
+
pending_tasks?: number;
|
|
9
|
+
total_agents?: number;
|
|
10
|
+
active_agents?: number;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const statsCommand = new Command("stats")
|
|
15
|
+
.description("Show usage statistics")
|
|
16
|
+
.option("--json", "Output raw JSON")
|
|
17
|
+
.action(async (opts) => {
|
|
18
|
+
const data = await apiCall<Stats>("GET", "/v1/stats");
|
|
19
|
+
|
|
20
|
+
if (opts.json) {
|
|
21
|
+
console.log(JSON.stringify(data, null, 2));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
console.log();
|
|
26
|
+
if (data.total_tasks !== undefined) label("Total Tasks", String(data.total_tasks));
|
|
27
|
+
if (data.completed_tasks !== undefined) label("Completed", String(data.completed_tasks));
|
|
28
|
+
if (data.pending_tasks !== undefined) label("Pending", String(data.pending_tasks));
|
|
29
|
+
if (data.total_agents !== undefined) label("Total Agents", String(data.total_agents));
|
|
30
|
+
if (data.active_agents !== undefined) label("Active Agents", String(data.active_agents));
|
|
31
|
+
|
|
32
|
+
// Print any additional stats fields
|
|
33
|
+
const knownKeys = new Set([
|
|
34
|
+
"total_tasks",
|
|
35
|
+
"completed_tasks",
|
|
36
|
+
"pending_tasks",
|
|
37
|
+
"total_agents",
|
|
38
|
+
"active_agents",
|
|
39
|
+
]);
|
|
40
|
+
for (const [key, value] of Object.entries(data)) {
|
|
41
|
+
if (!knownKeys.has(key) && value !== undefined) {
|
|
42
|
+
const displayKey = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
43
|
+
const displayVal = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
44
|
+
label(displayKey, displayVal);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
console.log();
|
|
48
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import node_readline from "node:readline";
|
|
3
|
+
import { apiCall } from "../api.js";
|
|
4
|
+
import {
|
|
5
|
+
printTable,
|
|
6
|
+
formatDate,
|
|
7
|
+
formatId,
|
|
8
|
+
priorityBadge,
|
|
9
|
+
statusBadge,
|
|
10
|
+
label,
|
|
11
|
+
} from "../ui.js";
|
|
12
|
+
|
|
13
|
+
interface Task {
|
|
14
|
+
id: string;
|
|
15
|
+
content: string;
|
|
16
|
+
status: string;
|
|
17
|
+
priority: number;
|
|
18
|
+
labels?: string[];
|
|
19
|
+
due_date?: string;
|
|
20
|
+
created_at?: string;
|
|
21
|
+
updated_at?: string;
|
|
22
|
+
completed_at?: string;
|
|
23
|
+
assigned_to_agent_id?: string;
|
|
24
|
+
comments?: Comment[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Comment {
|
|
28
|
+
id: string;
|
|
29
|
+
content: string;
|
|
30
|
+
created_at?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function confirm(question: string): Promise<boolean> {
|
|
34
|
+
const rl = node_readline.createInterface({
|
|
35
|
+
input: process.stdin,
|
|
36
|
+
output: process.stdout,
|
|
37
|
+
});
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
rl.question(question, (answer) => {
|
|
40
|
+
rl.close();
|
|
41
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const tasksList = new Command("list")
|
|
47
|
+
.description("List tasks")
|
|
48
|
+
.option("--completed", "Include completed tasks")
|
|
49
|
+
.option("--limit <n>", "Limit results", parseInt)
|
|
50
|
+
.option("--json", "Output raw JSON")
|
|
51
|
+
.action(async (opts) => {
|
|
52
|
+
let path = "/v1/tasks";
|
|
53
|
+
const params: string[] = [];
|
|
54
|
+
if (opts.completed) params.push("completed=true");
|
|
55
|
+
if (opts.limit) params.push(`limit=${opts.limit}`);
|
|
56
|
+
if (params.length > 0) path += "?" + params.join("&");
|
|
57
|
+
|
|
58
|
+
const data = await apiCall<Task[]>("GET", path);
|
|
59
|
+
|
|
60
|
+
if (opts.json) {
|
|
61
|
+
console.log(JSON.stringify(data, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const tasks = Array.isArray(data) ? data : [];
|
|
66
|
+
|
|
67
|
+
if (tasks.length === 0) {
|
|
68
|
+
console.log("No tasks found.");
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const headers = ["ID", "Priority", "Status", "Content"];
|
|
73
|
+
const rows = tasks.map((t) => [
|
|
74
|
+
formatId(t.id),
|
|
75
|
+
priorityBadge(t.priority),
|
|
76
|
+
statusBadge(t.status),
|
|
77
|
+
t.content.length > 50 ? t.content.slice(0, 47) + "..." : t.content,
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
printTable(headers, rows);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const tasksCreate = new Command("create")
|
|
84
|
+
.description("Create a new task")
|
|
85
|
+
.argument("<content>", "Task content")
|
|
86
|
+
.option("--priority <n>", "Priority 1-4 (default: 1)", parseInt, 1)
|
|
87
|
+
.option("--labels <labels>", "Comma-separated labels")
|
|
88
|
+
.option("--due <date>", "Due date (YYYY-MM-DD)")
|
|
89
|
+
.option("--json", "Output raw JSON")
|
|
90
|
+
.action(async (content: string, opts) => {
|
|
91
|
+
const body: Record<string, unknown> = { content };
|
|
92
|
+
if (opts.priority) body.priority = opts.priority;
|
|
93
|
+
if (opts.labels) body.labels = opts.labels.split(",").map((l: string) => l.trim());
|
|
94
|
+
if (opts.due) body.due_date = opts.due;
|
|
95
|
+
|
|
96
|
+
const task = await apiCall<Task>("POST", "/v1/tasks", body);
|
|
97
|
+
|
|
98
|
+
if (opts.json) {
|
|
99
|
+
console.log(JSON.stringify(task, null, 2));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(`Task created: ${task.id}`);
|
|
104
|
+
console.log();
|
|
105
|
+
label("Content", task.content);
|
|
106
|
+
label("Priority", priorityBadge(task.priority));
|
|
107
|
+
label("Status", statusBadge(task.status));
|
|
108
|
+
if (task.due_date) label("Due", formatDate(task.due_date));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const tasksShow = new Command("show")
|
|
112
|
+
.description("Show task details")
|
|
113
|
+
.argument("<id>", "Task ID")
|
|
114
|
+
.option("--json", "Output raw JSON")
|
|
115
|
+
.action(async (id: string, opts) => {
|
|
116
|
+
const task = await apiCall<Task>("GET", `/v1/tasks/${id}`);
|
|
117
|
+
|
|
118
|
+
if (opts.json) {
|
|
119
|
+
console.log(JSON.stringify(task, null, 2));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.log();
|
|
124
|
+
label("ID", task.id);
|
|
125
|
+
label("Content", task.content);
|
|
126
|
+
label("Status", statusBadge(task.status));
|
|
127
|
+
label("Priority", priorityBadge(task.priority));
|
|
128
|
+
const labels = typeof task.labels === "string" ? JSON.parse(task.labels) : task.labels;
|
|
129
|
+
if (labels && labels.length > 0) {
|
|
130
|
+
label("Labels", labels.join(", "));
|
|
131
|
+
}
|
|
132
|
+
if (task.due_date) label("Due", formatDate(task.due_date));
|
|
133
|
+
if (task.assigned_to_agent_id) label("Assigned To", task.assigned_to_agent_id);
|
|
134
|
+
label("Created", formatDate(task.created_at || ""));
|
|
135
|
+
if (task.updated_at) label("Updated", formatDate(task.updated_at));
|
|
136
|
+
if (task.completed_at) label("Completed", formatDate(task.completed_at));
|
|
137
|
+
|
|
138
|
+
if (task.comments && task.comments.length > 0) {
|
|
139
|
+
console.log();
|
|
140
|
+
console.log("Comments:");
|
|
141
|
+
for (const c of task.comments) {
|
|
142
|
+
console.log(` [${formatDate(c.created_at || "")}] ${c.content}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
console.log();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const tasksComplete = new Command("complete")
|
|
149
|
+
.description("Mark a task as completed")
|
|
150
|
+
.argument("<id>", "Task ID")
|
|
151
|
+
.action(async (id: string) => {
|
|
152
|
+
await apiCall("POST", `/v1/tasks/${id}/complete`);
|
|
153
|
+
console.log(`Task ${id} completed.`);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const tasksDelete = new Command("delete")
|
|
157
|
+
.description("Delete a task")
|
|
158
|
+
.argument("<id>", "Task ID")
|
|
159
|
+
.action(async (id: string) => {
|
|
160
|
+
const yes = await confirm(`Delete task ${id}? (y/N) `);
|
|
161
|
+
if (!yes) {
|
|
162
|
+
console.log("Cancelled.");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
await apiCall("DELETE", `/v1/tasks/${id}`);
|
|
166
|
+
console.log(`Task ${id} deleted.`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const tasksDelegate = new Command("delegate")
|
|
170
|
+
.description("Delegate a task to another agent")
|
|
171
|
+
.argument("<task_id>", "Task ID")
|
|
172
|
+
.argument("<agent_id>", "Agent ID to delegate to")
|
|
173
|
+
.requiredOption("--content <content>", "Subtask description (required)")
|
|
174
|
+
.action(async (taskId: string, agentId: string, opts) => {
|
|
175
|
+
const body: Record<string, unknown> = { assigned_to_agent_id: agentId };
|
|
176
|
+
if (opts.content) body.content = opts.content;
|
|
177
|
+
|
|
178
|
+
await apiCall("POST", `/v1/tasks/${taskId}/delegate`, body);
|
|
179
|
+
console.log(`Task delegated to ${agentId}.`);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export const tasksCommand = new Command("tasks")
|
|
183
|
+
.description("Manage tasks")
|
|
184
|
+
.addCommand(tasksList)
|
|
185
|
+
.addCommand(tasksCreate)
|
|
186
|
+
.addCommand(tasksShow)
|
|
187
|
+
.addCommand(tasksComplete)
|
|
188
|
+
.addCommand(tasksDelete)
|
|
189
|
+
.addCommand(tasksDelegate);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { apiCall } from "../api.js";
|
|
3
|
+
import { label } from "../ui.js";
|
|
4
|
+
|
|
5
|
+
interface Agent {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
display_name?: string;
|
|
9
|
+
email?: string;
|
|
10
|
+
plan?: string;
|
|
11
|
+
active?: boolean;
|
|
12
|
+
user?: {
|
|
13
|
+
email?: string;
|
|
14
|
+
plan?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const whoamiCommand = new Command("whoami")
|
|
19
|
+
.description("Show current authenticated agent")
|
|
20
|
+
.action(async () => {
|
|
21
|
+
const data = await apiCall<Agent | Agent[]>("GET", "/v1/agents");
|
|
22
|
+
|
|
23
|
+
let agent: Agent;
|
|
24
|
+
if (Array.isArray(data)) {
|
|
25
|
+
if (data.length === 0) {
|
|
26
|
+
console.error("No agent found.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
agent = data[0];
|
|
30
|
+
} else {
|
|
31
|
+
agent = data;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
console.log();
|
|
35
|
+
label("Agent", agent.name);
|
|
36
|
+
if (agent.display_name) {
|
|
37
|
+
label("Display Name", agent.display_name);
|
|
38
|
+
}
|
|
39
|
+
if (agent.user?.email || agent.email) {
|
|
40
|
+
label("Email", agent.user?.email || agent.email || "");
|
|
41
|
+
}
|
|
42
|
+
if (agent.user?.plan || agent.plan) {
|
|
43
|
+
label("Plan", agent.user?.plan || agent.plan || "");
|
|
44
|
+
}
|
|
45
|
+
label("Active", agent.active !== false ? "yes" : "no");
|
|
46
|
+
console.log();
|
|
47
|
+
});
|