@otto-code/cli 0.5.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/bin/otto +3 -0
- package/dist/classify.d.ts +19 -0
- package/dist/classify.js +49 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +113 -0
- package/dist/commands/agent/archive.d.ts +18 -0
- package/dist/commands/agent/archive.js +102 -0
- package/dist/commands/agent/attach.d.ts +11 -0
- package/dist/commands/agent/attach.js +159 -0
- package/dist/commands/agent/delete.d.ts +15 -0
- package/dist/commands/agent/delete.js +107 -0
- package/dist/commands/agent/import.d.ts +14 -0
- package/dist/commands/agent/import.js +122 -0
- package/dist/commands/agent/index.d.ts +3 -0
- package/dist/commands/agent/index.js +48 -0
- package/dist/commands/agent/inspect.d.ts +15 -0
- package/dist/commands/agent/inspect.js +209 -0
- package/dist/commands/agent/logs.d.ts +19 -0
- package/dist/commands/agent/logs.js +180 -0
- package/dist/commands/agent/ls.d.ts +44 -0
- package/dist/commands/agent/ls.js +204 -0
- package/dist/commands/agent/mode.d.ts +18 -0
- package/dist/commands/agent/mode.js +100 -0
- package/dist/commands/agent/reload.d.ts +15 -0
- package/dist/commands/agent/reload.js +75 -0
- package/dist/commands/agent/run.d.ts +44 -0
- package/dist/commands/agent/run.js +464 -0
- package/dist/commands/agent/send.d.ts +19 -0
- package/dist/commands/agent/send.js +187 -0
- package/dist/commands/agent/stop.d.ts +17 -0
- package/dist/commands/agent/stop.js +115 -0
- package/dist/commands/agent/update.d.ts +18 -0
- package/dist/commands/agent/update.js +139 -0
- package/dist/commands/agent/wait.d.ts +17 -0
- package/dist/commands/agent/wait.js +158 -0
- package/dist/commands/chat/create.d.ts +9 -0
- package/dist/commands/chat/create.js +23 -0
- package/dist/commands/chat/delete.d.ts +6 -0
- package/dist/commands/chat/delete.js +20 -0
- package/dist/commands/chat/index.d.ts +3 -0
- package/dist/commands/chat/index.js +47 -0
- package/dist/commands/chat/inspect.d.ts +6 -0
- package/dist/commands/chat/inspect.js +20 -0
- package/dist/commands/chat/ls.d.ts +6 -0
- package/dist/commands/chat/ls.js +20 -0
- package/dist/commands/chat/post.d.ts +9 -0
- package/dist/commands/chat/post.js +28 -0
- package/dist/commands/chat/read.d.ts +11 -0
- package/dist/commands/chat/read.js +40 -0
- package/dist/commands/chat/schema.d.ts +36 -0
- package/dist/commands/chat/schema.js +81 -0
- package/dist/commands/chat/shared.d.ts +19 -0
- package/dist/commands/chat/shared.js +118 -0
- package/dist/commands/chat/wait.d.ts +9 -0
- package/dist/commands/chat/wait.js +45 -0
- package/dist/commands/daemon/index.d.ts +3 -0
- package/dist/commands/daemon/index.js +57 -0
- package/dist/commands/daemon/local-daemon.d.ts +78 -0
- package/dist/commands/daemon/local-daemon.js +544 -0
- package/dist/commands/daemon/pair.d.ts +9 -0
- package/dist/commands/daemon/pair.js +76 -0
- package/dist/commands/daemon/restart.d.ts +12 -0
- package/dist/commands/daemon/restart.js +99 -0
- package/dist/commands/daemon/runtime-toolchain.d.ts +6 -0
- package/dist/commands/daemon/runtime-toolchain.js +87 -0
- package/dist/commands/daemon/set-password.d.ts +17 -0
- package/dist/commands/daemon/set-password.js +82 -0
- package/dist/commands/daemon/start.d.ts +6 -0
- package/dist/commands/daemon/start.js +56 -0
- package/dist/commands/daemon/status.d.ts +10 -0
- package/dist/commands/daemon/status.js +338 -0
- package/dist/commands/daemon/stop.d.ts +15 -0
- package/dist/commands/daemon/stop.js +60 -0
- package/dist/commands/hooks.d.ts +19 -0
- package/dist/commands/hooks.js +89 -0
- package/dist/commands/loop/index.d.ts +3 -0
- package/dist/commands/loop/index.js +18 -0
- package/dist/commands/loop/inspect.d.ts +13 -0
- package/dist/commands/loop/inspect.js +100 -0
- package/dist/commands/loop/logs.d.ts +8 -0
- package/dist/commands/loop/logs.js +69 -0
- package/dist/commands/loop/ls.d.ts +18 -0
- package/dist/commands/loop/ls.js +62 -0
- package/dist/commands/loop/run.d.ts +30 -0
- package/dist/commands/loop/run.js +162 -0
- package/dist/commands/loop/stop.d.ts +15 -0
- package/dist/commands/loop/stop.js +56 -0
- package/dist/commands/loop/types.d.ts +138 -0
- package/dist/commands/loop/types.js +2 -0
- package/dist/commands/onboard.d.ts +10 -0
- package/dist/commands/onboard.js +416 -0
- package/dist/commands/open.d.ts +2 -0
- package/dist/commands/open.js +81 -0
- package/dist/commands/permit/allow.d.ts +20 -0
- package/dist/commands/permit/allow.js +133 -0
- package/dist/commands/permit/deny.d.ts +12 -0
- package/dist/commands/permit/deny.js +105 -0
- package/dist/commands/permit/index.d.ts +3 -0
- package/dist/commands/permit/index.js +27 -0
- package/dist/commands/permit/ls.d.ts +18 -0
- package/dist/commands/permit/ls.js +66 -0
- package/dist/commands/provider/index.d.ts +3 -0
- package/dist/commands/provider/index.js +16 -0
- package/dist/commands/provider/ls.d.ts +19 -0
- package/dist/commands/provider/ls.js +73 -0
- package/dist/commands/provider/models.d.ts +20 -0
- package/dist/commands/provider/models.js +53 -0
- package/dist/commands/schedule/create.d.ts +18 -0
- package/dist/commands/schedule/create.js +40 -0
- package/dist/commands/schedule/delete.d.ts +10 -0
- package/dist/commands/schedule/delete.js +32 -0
- package/dist/commands/schedule/index.d.ts +3 -0
- package/dist/commands/schedule/index.js +66 -0
- package/dist/commands/schedule/inspect.d.ts +6 -0
- package/dist/commands/schedule/inspect.js +24 -0
- package/dist/commands/schedule/logs.d.ts +6 -0
- package/dist/commands/schedule/logs.js +23 -0
- package/dist/commands/schedule/ls.d.ts +5 -0
- package/dist/commands/schedule/ls.js +23 -0
- package/dist/commands/schedule/pause.d.ts +5 -0
- package/dist/commands/schedule/pause.js +23 -0
- package/dist/commands/schedule/resume.d.ts +5 -0
- package/dist/commands/schedule/resume.js +23 -0
- package/dist/commands/schedule/run-once.d.ts +5 -0
- package/dist/commands/schedule/run-once.js +23 -0
- package/dist/commands/schedule/schema.d.ts +21 -0
- package/dist/commands/schedule/schema.js +67 -0
- package/dist/commands/schedule/shared.d.ts +56 -0
- package/dist/commands/schedule/shared.js +346 -0
- package/dist/commands/schedule/types.d.ts +168 -0
- package/dist/commands/schedule/types.js +2 -0
- package/dist/commands/schedule/update.d.ts +21 -0
- package/dist/commands/schedule/update.js +39 -0
- package/dist/commands/speech/index.d.ts +3 -0
- package/dist/commands/speech/index.js +5 -0
- package/dist/commands/terminal/capture.d.ts +10 -0
- package/dist/commands/terminal/capture.js +68 -0
- package/dist/commands/terminal/create.d.ts +10 -0
- package/dist/commands/terminal/create.js +38 -0
- package/dist/commands/terminal/index.d.ts +3 -0
- package/dist/commands/terminal/index.js +43 -0
- package/dist/commands/terminal/kill.d.ts +6 -0
- package/dist/commands/terminal/kill.js +36 -0
- package/dist/commands/terminal/ls.d.ts +10 -0
- package/dist/commands/terminal/ls.js +21 -0
- package/dist/commands/terminal/schema.d.ts +18 -0
- package/dist/commands/terminal/schema.js +23 -0
- package/dist/commands/terminal/send-keys.d.ts +7 -0
- package/dist/commands/terminal/send-keys.js +76 -0
- package/dist/commands/terminal/shared.d.ts +12 -0
- package/dist/commands/terminal/shared.js +61 -0
- package/dist/commands/worktree/archive.d.ts +20 -0
- package/dist/commands/worktree/archive.js +104 -0
- package/dist/commands/worktree/create-input.d.ts +15 -0
- package/dist/commands/worktree/create-input.js +56 -0
- package/dist/commands/worktree/create.d.ts +11 -0
- package/dist/commands/worktree/create.js +58 -0
- package/dist/commands/worktree/index.d.ts +3 -0
- package/dist/commands/worktree/index.js +25 -0
- package/dist/commands/worktree/ls.d.ts +19 -0
- package/dist/commands/worktree/ls.js +97 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/output/index.d.ts +45 -0
- package/dist/output/index.js +47 -0
- package/dist/output/json.d.ts +11 -0
- package/dist/output/json.js +37 -0
- package/dist/output/quiet.d.ts +9 -0
- package/dist/output/quiet.js +22 -0
- package/dist/output/render.d.ts +15 -0
- package/dist/output/render.js +83 -0
- package/dist/output/table.d.ts +13 -0
- package/dist/output/table.js +135 -0
- package/dist/output/types.d.ts +73 -0
- package/dist/output/types.js +8 -0
- package/dist/output/with-output.d.ts +41 -0
- package/dist/output/with-output.js +87 -0
- package/dist/output/yaml.d.ts +11 -0
- package/dist/output/yaml.js +38 -0
- package/dist/run.d.ts +14 -0
- package/dist/run.js +33 -0
- package/dist/utils/client-id.d.ts +2 -0
- package/dist/utils/client-id.js +37 -0
- package/dist/utils/client.d.ts +44 -0
- package/dist/utils/client.js +330 -0
- package/dist/utils/command-options.d.ts +6 -0
- package/dist/utils/command-options.js +17 -0
- package/dist/utils/duration.d.ts +7 -0
- package/dist/utils/duration.js +38 -0
- package/dist/utils/errors.d.ts +5 -0
- package/dist/utils/errors.js +10 -0
- package/dist/utils/paths.d.ts +12 -0
- package/dist/utils/paths.js +22 -0
- package/dist/utils/provider-model.d.ts +11 -0
- package/dist/utils/provider-model.js +49 -0
- package/dist/utils/timeline.d.ts +11 -0
- package/dist/utils/timeline.js +11 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +10 -0
- package/package.json +48 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
|
|
2
|
+
import { isSameOrDescendantPath } from "../../utils/paths.js";
|
|
3
|
+
/** Schema for stop command output */
|
|
4
|
+
export const stopSchema = {
|
|
5
|
+
// For quiet mode, output the stopped agent IDs (one per line)
|
|
6
|
+
idField: (item) => item.agentIds.join("\n"),
|
|
7
|
+
columns: [{ header: "INTERRUPTED", field: "stoppedCount" }],
|
|
8
|
+
};
|
|
9
|
+
export function addStopOptions(cmd) {
|
|
10
|
+
return cmd
|
|
11
|
+
.description("Interrupt an agent if it is running (no-op for idle agents)")
|
|
12
|
+
.argument("[id]", "Agent ID (or prefix) - optional if --all or --cwd specified")
|
|
13
|
+
.option("--all", "Stop all agents")
|
|
14
|
+
.option("--cwd <path>", "Stop all agents in directory");
|
|
15
|
+
}
|
|
16
|
+
export async function runStopCommand(id, options, _command) {
|
|
17
|
+
const host = getDaemonHost({ host: options.host });
|
|
18
|
+
// Validate arguments - need either an id, --all, or --cwd
|
|
19
|
+
if (!id && !options.all && !options.cwd) {
|
|
20
|
+
const error = {
|
|
21
|
+
code: "MISSING_ARGUMENT",
|
|
22
|
+
message: "Agent ID required unless --all or --cwd is specified",
|
|
23
|
+
details: "Usage: otto agent stop <id> | --all | --cwd <path>",
|
|
24
|
+
};
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
let client;
|
|
28
|
+
try {
|
|
29
|
+
client = await connectToDaemon({ host: options.host });
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
33
|
+
const error = {
|
|
34
|
+
code: "DAEMON_NOT_RUNNING",
|
|
35
|
+
message: `Cannot connect to daemon at ${host}: ${message}`,
|
|
36
|
+
details: "Start the daemon with: otto daemon start",
|
|
37
|
+
};
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const fetchPayload = await client.fetchAgents({ filter: { includeArchived: true } });
|
|
42
|
+
let agents = fetchPayload.entries.map((entry) => entry.agent);
|
|
43
|
+
const stoppedIds = [];
|
|
44
|
+
if (options.all) {
|
|
45
|
+
// Stop all agents (not archived)
|
|
46
|
+
agents = agents.filter((a) => !a.archivedAt);
|
|
47
|
+
}
|
|
48
|
+
else if (options.cwd) {
|
|
49
|
+
// Stop agents in directory
|
|
50
|
+
agents = agents.filter((a) => {
|
|
51
|
+
if (a.archivedAt)
|
|
52
|
+
return false;
|
|
53
|
+
return isSameOrDescendantPath(options.cwd, a.cwd);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
else if (id) {
|
|
57
|
+
// Stop specific agent
|
|
58
|
+
const fetchResult = await client.fetchAgent({ agentId: id });
|
|
59
|
+
if (!fetchResult) {
|
|
60
|
+
const error = {
|
|
61
|
+
code: "AGENT_NOT_FOUND",
|
|
62
|
+
message: `No agent found matching: ${id}`,
|
|
63
|
+
details: "Use `otto ls` to list available agents",
|
|
64
|
+
};
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
agents = [fetchResult.agent];
|
|
68
|
+
}
|
|
69
|
+
// Interrupt each running agent. Idle agents are a no-op.
|
|
70
|
+
const stopResults = await Promise.all(agents.map(async (agent) => {
|
|
71
|
+
if (agent.status !== "running")
|
|
72
|
+
return { ok: true, id: agent.id, stopped: false };
|
|
73
|
+
try {
|
|
74
|
+
await client.cancelAgent(agent.id);
|
|
75
|
+
return { ok: true, id: agent.id, stopped: true };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
79
|
+
return { ok: false, id: agent.id, message };
|
|
80
|
+
}
|
|
81
|
+
}));
|
|
82
|
+
for (const result of stopResults) {
|
|
83
|
+
if (!result.ok) {
|
|
84
|
+
console.error(`Warning: Failed to stop agent ${result.id.slice(0, 7)}: ${result.message}`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (result.stopped) {
|
|
88
|
+
stoppedIds.push(result.id);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
await client.close();
|
|
92
|
+
return {
|
|
93
|
+
type: "single",
|
|
94
|
+
data: {
|
|
95
|
+
stoppedCount: stoppedIds.length,
|
|
96
|
+
agentIds: stoppedIds,
|
|
97
|
+
},
|
|
98
|
+
schema: stopSchema,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
await client.close().catch(() => { });
|
|
103
|
+
// Re-throw if it's already a CommandError
|
|
104
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
108
|
+
const error = {
|
|
109
|
+
code: "STOP_AGENT_FAILED",
|
|
110
|
+
message: `Failed to stop agent(s): ${message}`,
|
|
111
|
+
};
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=stop.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
|
|
3
|
+
/** Result type for agent update command */
|
|
4
|
+
export interface AgentUpdateResult {
|
|
5
|
+
agentId: string;
|
|
6
|
+
name: string | null;
|
|
7
|
+
labels: string;
|
|
8
|
+
}
|
|
9
|
+
/** Schema for update command output */
|
|
10
|
+
export declare const updateSchema: OutputSchema<AgentUpdateResult>;
|
|
11
|
+
export interface AgentUpdateOptions extends CommandOptions {
|
|
12
|
+
name?: string;
|
|
13
|
+
label?: string[];
|
|
14
|
+
host?: string;
|
|
15
|
+
}
|
|
16
|
+
export type AgentUpdateCommandResult = SingleResult<AgentUpdateResult>;
|
|
17
|
+
export declare function runUpdateCommand(agentIdArg: string, options: AgentUpdateOptions, _command: Command): Promise<AgentUpdateCommandResult>;
|
|
18
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
|
|
2
|
+
/** Schema for update command output */
|
|
3
|
+
export const updateSchema = {
|
|
4
|
+
idField: "agentId",
|
|
5
|
+
columns: [
|
|
6
|
+
{ header: "AGENT ID", field: "agentId" },
|
|
7
|
+
{ header: "NAME", field: "name" },
|
|
8
|
+
{ header: "LABELS", field: "labels" },
|
|
9
|
+
],
|
|
10
|
+
};
|
|
11
|
+
function parseLabelOptions(labels) {
|
|
12
|
+
const parsed = {};
|
|
13
|
+
if (!labels) {
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
for (const rawLabel of labels) {
|
|
17
|
+
for (const segment of rawLabel.split(",")) {
|
|
18
|
+
const label = segment.trim();
|
|
19
|
+
if (!label) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const eqIndex = label.indexOf("=");
|
|
23
|
+
if (eqIndex === -1) {
|
|
24
|
+
const error = {
|
|
25
|
+
code: "INVALID_LABEL",
|
|
26
|
+
message: `Invalid label format: ${label}`,
|
|
27
|
+
details: "Labels must be in key=value format",
|
|
28
|
+
};
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
const key = label.slice(0, eqIndex).trim();
|
|
32
|
+
const value = label.slice(eqIndex + 1);
|
|
33
|
+
if (!key) {
|
|
34
|
+
const error = {
|
|
35
|
+
code: "INVALID_LABEL",
|
|
36
|
+
message: `Invalid label format: ${label}`,
|
|
37
|
+
details: "Labels must include a non-empty key in key=value format",
|
|
38
|
+
};
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
parsed[key] = value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return parsed;
|
|
45
|
+
}
|
|
46
|
+
function formatLabels(labels) {
|
|
47
|
+
const entries = Object.entries(labels);
|
|
48
|
+
if (entries.length === 0) {
|
|
49
|
+
return "-";
|
|
50
|
+
}
|
|
51
|
+
return entries.map(([key, value]) => `${key}=${value}`).join(",");
|
|
52
|
+
}
|
|
53
|
+
export async function runUpdateCommand(agentIdArg, options, _command) {
|
|
54
|
+
const host = getDaemonHost({ host: options.host });
|
|
55
|
+
// Validate arguments
|
|
56
|
+
if (!agentIdArg || agentIdArg.trim().length === 0) {
|
|
57
|
+
const error = {
|
|
58
|
+
code: "MISSING_AGENT_ID",
|
|
59
|
+
message: "Agent ID is required",
|
|
60
|
+
details: "Usage: otto agent update <id> [--name <name>] [--label <key=value>]",
|
|
61
|
+
};
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
const name = options.name?.trim();
|
|
65
|
+
if (options.name !== undefined && !name) {
|
|
66
|
+
const error = {
|
|
67
|
+
code: "INVALID_NAME",
|
|
68
|
+
message: "Name cannot be empty",
|
|
69
|
+
details: "Use --name <name> with a non-empty value",
|
|
70
|
+
};
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
const labels = parseLabelOptions(options.label);
|
|
74
|
+
if (!name && Object.keys(labels).length === 0) {
|
|
75
|
+
const error = {
|
|
76
|
+
code: "NO_CHANGES_PROVIDED",
|
|
77
|
+
message: "Nothing to update",
|
|
78
|
+
details: "Provide at least one of: --name <name>, --label <key=value>",
|
|
79
|
+
};
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
let client;
|
|
83
|
+
try {
|
|
84
|
+
client = await connectToDaemon({ host: options.host });
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
88
|
+
const error = {
|
|
89
|
+
code: "DAEMON_NOT_RUNNING",
|
|
90
|
+
message: `Cannot connect to daemon at ${host}: ${message}`,
|
|
91
|
+
details: "Start the daemon with: otto daemon start",
|
|
92
|
+
};
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const fetchResult = await client.fetchAgent({ agentId: agentIdArg });
|
|
97
|
+
if (!fetchResult) {
|
|
98
|
+
const error = {
|
|
99
|
+
code: "AGENT_NOT_FOUND",
|
|
100
|
+
message: `Agent not found: ${agentIdArg}`,
|
|
101
|
+
details: 'Use "otto ls" to list available agents',
|
|
102
|
+
};
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
const agentId = fetchResult.agent.id;
|
|
106
|
+
await client.updateAgent(agentId, {
|
|
107
|
+
...(name ? { name } : {}),
|
|
108
|
+
...(Object.keys(labels).length > 0 ? { labels } : {}),
|
|
109
|
+
});
|
|
110
|
+
const updatedResult = await client.fetchAgent({ agentId });
|
|
111
|
+
if (!updatedResult) {
|
|
112
|
+
throw new Error(`Agent not found after update: ${agentId}`);
|
|
113
|
+
}
|
|
114
|
+
await client.close();
|
|
115
|
+
return {
|
|
116
|
+
type: "single",
|
|
117
|
+
data: {
|
|
118
|
+
agentId,
|
|
119
|
+
name: updatedResult.agent.title,
|
|
120
|
+
labels: formatLabels(updatedResult.agent.labels),
|
|
121
|
+
},
|
|
122
|
+
schema: updateSchema,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
await client.close().catch(() => { });
|
|
127
|
+
// Re-throw CommandError as-is
|
|
128
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
132
|
+
const error = {
|
|
133
|
+
code: "UPDATE_FAILED",
|
|
134
|
+
message: `Failed to update agent: ${message}`,
|
|
135
|
+
};
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
|
|
3
|
+
/** Result type for agent wait command */
|
|
4
|
+
export interface AgentWaitResult {
|
|
5
|
+
agentId: string;
|
|
6
|
+
status: "idle" | "timeout" | "permission" | "error";
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
/** Schema for agent wait output */
|
|
10
|
+
export declare const agentWaitSchema: OutputSchema<AgentWaitResult>;
|
|
11
|
+
export interface AgentWaitOptions extends CommandOptions {
|
|
12
|
+
timeout?: string;
|
|
13
|
+
host?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function addWaitOptions(cmd: Command): Command;
|
|
16
|
+
export declare function runWaitCommand(agentIdArg: string, options: AgentWaitOptions, _command: Command): Promise<SingleResult<AgentWaitResult>>;
|
|
17
|
+
//# sourceMappingURL=wait.d.ts.map
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
|
|
2
|
+
import { fetchAgentTimelineItems, formatAgentActivityTranscript } from "./logs.js";
|
|
3
|
+
import { parseDuration } from "../../utils/duration.js";
|
|
4
|
+
/** Schema for agent wait output */
|
|
5
|
+
export const agentWaitSchema = {
|
|
6
|
+
idField: "agentId",
|
|
7
|
+
columns: [
|
|
8
|
+
{ header: "AGENT ID", field: "agentId", width: 12 },
|
|
9
|
+
{ header: "STATUS", field: "status", width: 12 },
|
|
10
|
+
{ header: "MESSAGE", field: "message", width: 40 },
|
|
11
|
+
],
|
|
12
|
+
};
|
|
13
|
+
const WAIT_ACTIVITY_PREVIEW_COUNT = 5;
|
|
14
|
+
const WAIT_ACTIVITY_PREVIEW_TIMEOUT_MS = 2000;
|
|
15
|
+
function appendRecentActivity(message, transcript) {
|
|
16
|
+
if (!transcript || transcript.trim().length === 0) {
|
|
17
|
+
return message;
|
|
18
|
+
}
|
|
19
|
+
return `${message}\nLast ${WAIT_ACTIVITY_PREVIEW_COUNT} activity items:\n${transcript}`;
|
|
20
|
+
}
|
|
21
|
+
async function getRecentActivityTranscript(client, agentId) {
|
|
22
|
+
try {
|
|
23
|
+
const timelineItems = await fetchAgentTimelineItems(client, agentId, {
|
|
24
|
+
timeoutMs: WAIT_ACTIVITY_PREVIEW_TIMEOUT_MS,
|
|
25
|
+
});
|
|
26
|
+
return formatAgentActivityTranscript(timelineItems, WAIT_ACTIVITY_PREVIEW_COUNT);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseWaitTimeout(timeout) {
|
|
33
|
+
if (!timeout)
|
|
34
|
+
return { timeoutMs: 0, timeoutLabel: null };
|
|
35
|
+
try {
|
|
36
|
+
const ms = parseDuration(timeout);
|
|
37
|
+
if (ms <= 0) {
|
|
38
|
+
throw new Error("Timeout must be positive");
|
|
39
|
+
}
|
|
40
|
+
const timeoutSeconds = Math.floor(ms / 1000);
|
|
41
|
+
return {
|
|
42
|
+
timeoutMs: ms,
|
|
43
|
+
timeoutLabel: `${timeoutSeconds} second${timeoutSeconds === 1 ? "" : "s"}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
48
|
+
throw {
|
|
49
|
+
code: "INVALID_TIMEOUT",
|
|
50
|
+
message: "Invalid timeout value",
|
|
51
|
+
details: message,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function buildWaitResult(args) {
|
|
56
|
+
const { state, resolvedAgentId, recentActivity, timeoutLabel } = args;
|
|
57
|
+
if (state.status === "timeout") {
|
|
58
|
+
const timeoutMessage = timeoutLabel
|
|
59
|
+
? `Agent did not finish within ${timeoutLabel}. Run \`otto wait ${resolvedAgentId}\` again to keep waiting.`
|
|
60
|
+
: `Agent wait timed out. Run \`otto wait ${resolvedAgentId}\` again to keep waiting.`;
|
|
61
|
+
return {
|
|
62
|
+
agentId: resolvedAgentId,
|
|
63
|
+
status: "timeout",
|
|
64
|
+
message: appendRecentActivity(timeoutMessage, recentActivity),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (state.status === "permission") {
|
|
68
|
+
const permission = state.final?.pendingPermissions?.[0];
|
|
69
|
+
return {
|
|
70
|
+
agentId: resolvedAgentId,
|
|
71
|
+
status: "permission",
|
|
72
|
+
message: permission
|
|
73
|
+
? `Agent is waiting for permission: ${permission.kind}`
|
|
74
|
+
: "Agent is waiting for permission",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (state.status === "error") {
|
|
78
|
+
return {
|
|
79
|
+
agentId: resolvedAgentId,
|
|
80
|
+
status: "error",
|
|
81
|
+
message: state.error ?? "Agent finished with error",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
agentId: resolvedAgentId,
|
|
86
|
+
status: "idle",
|
|
87
|
+
message: appendRecentActivity("Agent is idle.", recentActivity),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function addWaitOptions(cmd) {
|
|
91
|
+
return cmd
|
|
92
|
+
.description("Wait for an agent to become idle")
|
|
93
|
+
.argument("<id>", "Agent ID (or prefix)")
|
|
94
|
+
.option("--timeout <seconds>", "Maximum wait time (default: no limit)");
|
|
95
|
+
}
|
|
96
|
+
export async function runWaitCommand(agentIdArg, options, _command) {
|
|
97
|
+
const host = getDaemonHost({ host: options.host });
|
|
98
|
+
if (!agentIdArg || agentIdArg.trim().length === 0) {
|
|
99
|
+
throw {
|
|
100
|
+
code: "MISSING_AGENT_ID",
|
|
101
|
+
message: "Agent ID is required",
|
|
102
|
+
details: "Usage: otto agent wait <id>",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const { timeoutMs, timeoutLabel } = parseWaitTimeout(options.timeout);
|
|
106
|
+
let client;
|
|
107
|
+
try {
|
|
108
|
+
client = await connectToDaemon({ host: options.host });
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
112
|
+
const error = {
|
|
113
|
+
code: "DAEMON_NOT_RUNNING",
|
|
114
|
+
message: `Cannot connect to daemon at ${host}: ${message}`,
|
|
115
|
+
details: "Start the daemon with: otto daemon start",
|
|
116
|
+
};
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
try {
|
|
121
|
+
const state = await client.waitForFinish(agentIdArg, timeoutMs);
|
|
122
|
+
const resolvedAgentId = state.final?.id ?? agentIdArg;
|
|
123
|
+
const recentActivity = state.status === "timeout" || state.status === "idle"
|
|
124
|
+
? await getRecentActivityTranscript(client, resolvedAgentId)
|
|
125
|
+
: null;
|
|
126
|
+
await client.close();
|
|
127
|
+
return {
|
|
128
|
+
type: "single",
|
|
129
|
+
data: buildWaitResult({ state, resolvedAgentId, recentActivity, timeoutLabel }),
|
|
130
|
+
schema: agentWaitSchema,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
catch (waitErr) {
|
|
134
|
+
await client.close().catch(() => { });
|
|
135
|
+
const waitMessage = waitErr instanceof Error ? waitErr.message : String(waitErr);
|
|
136
|
+
// Other errors
|
|
137
|
+
const error = {
|
|
138
|
+
code: "WAIT_FAILED",
|
|
139
|
+
message: `Failed to wait for agent: ${waitMessage}`,
|
|
140
|
+
};
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
await client.close().catch(() => { });
|
|
146
|
+
// Re-throw CommandError as-is
|
|
147
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
151
|
+
const error = {
|
|
152
|
+
code: "WAIT_FAILED",
|
|
153
|
+
message: `Failed to wait for agent: ${message}`,
|
|
154
|
+
};
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=wait.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type ChatCommandOptions } from "./shared.js";
|
|
4
|
+
import { type ChatRoomRow } from "./schema.js";
|
|
5
|
+
export interface ChatCreateOptions extends ChatCommandOptions {
|
|
6
|
+
purpose?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function runCreateCommand(name: string, options: ChatCreateOptions, _command: Command): Promise<SingleResult<ChatRoomRow>>;
|
|
9
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { connectChatClient, toChatCommandError } from "./shared.js";
|
|
2
|
+
import { chatRoomSchema, toChatRoomRow } from "./schema.js";
|
|
3
|
+
export async function runCreateCommand(name, options, _command) {
|
|
4
|
+
const { client } = await connectChatClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const payload = await client.createChatRoom({
|
|
7
|
+
name,
|
|
8
|
+
purpose: options.purpose,
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
type: "single",
|
|
12
|
+
data: toChatRoomRow(payload.room),
|
|
13
|
+
schema: chatRoomSchema,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
throw toChatCommandError("CHAT_CREATE_FAILED", "create chat room", err);
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
await client.close().catch(() => { });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type ChatCommandOptions } from "./shared.js";
|
|
4
|
+
import { type ChatRoomRow } from "./schema.js";
|
|
5
|
+
export declare function runDeleteCommand(room: string, options: ChatCommandOptions, _command: Command): Promise<SingleResult<ChatRoomRow>>;
|
|
6
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { connectChatClient, toChatCommandError } from "./shared.js";
|
|
2
|
+
import { chatRoomSchema, toChatRoomRow } from "./schema.js";
|
|
3
|
+
export async function runDeleteCommand(room, options, _command) {
|
|
4
|
+
const { client } = await connectChatClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const payload = await client.deleteChatRoom({ room });
|
|
7
|
+
return {
|
|
8
|
+
type: "single",
|
|
9
|
+
data: toChatRoomRow(payload.room),
|
|
10
|
+
schema: chatRoomSchema,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
throw toChatCommandError("CHAT_DELETE_FAILED", "delete chat room", err);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await client.close().catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { withOutput } from "../../output/index.js";
|
|
3
|
+
import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
|
|
4
|
+
import { runCreateCommand } from "./create.js";
|
|
5
|
+
import { runLsCommand } from "./ls.js";
|
|
6
|
+
import { runInspectCommand } from "./inspect.js";
|
|
7
|
+
import { runDeleteCommand } from "./delete.js";
|
|
8
|
+
import { runPostCommand } from "./post.js";
|
|
9
|
+
import { runReadCommand } from "./read.js";
|
|
10
|
+
import { runWaitCommand } from "./wait.js";
|
|
11
|
+
export function createChatCommand() {
|
|
12
|
+
const chat = new Command("chat").description("Manage chat rooms for agent coordination");
|
|
13
|
+
addJsonAndDaemonHostOptions(chat
|
|
14
|
+
.command("create")
|
|
15
|
+
.description("Create a chat room")
|
|
16
|
+
.argument("<name>", "Room name (must be unique)")
|
|
17
|
+
.option("--purpose <text>", "Room purpose/description")).action(withOutput(runCreateCommand));
|
|
18
|
+
addJsonAndDaemonHostOptions(chat.command("ls").description("List chat rooms")).action(withOutput(runLsCommand));
|
|
19
|
+
addJsonAndDaemonHostOptions(chat
|
|
20
|
+
.command("inspect")
|
|
21
|
+
.description("Inspect a chat room")
|
|
22
|
+
.argument("<name-or-id>", "Room name or ID")).action(withOutput(runInspectCommand));
|
|
23
|
+
addJsonAndDaemonHostOptions(chat
|
|
24
|
+
.command("delete")
|
|
25
|
+
.description("Delete a chat room")
|
|
26
|
+
.argument("<name-or-id>", "Room name or ID")).action(withOutput(runDeleteCommand));
|
|
27
|
+
addJsonAndDaemonHostOptions(chat
|
|
28
|
+
.command("post")
|
|
29
|
+
.description("Post a chat message")
|
|
30
|
+
.argument("<name-or-id>", "Room name or ID")
|
|
31
|
+
.argument("<message>", "Message body")
|
|
32
|
+
.option("--reply-to <msg-id>", "Reply to a specific message ID")).action(withOutput(runPostCommand));
|
|
33
|
+
addJsonAndDaemonHostOptions(chat
|
|
34
|
+
.command("read")
|
|
35
|
+
.description("Read chat messages")
|
|
36
|
+
.argument("<name-or-id>", "Room name or ID")
|
|
37
|
+
.option("--limit <n>", "Maximum number of messages to return")
|
|
38
|
+
.option("--since <duration-or-timestamp>", "Filter by relative duration or ISO timestamp")
|
|
39
|
+
.option("--agent <agent-id>", "Filter by author agent ID")).action(withOutput(runReadCommand));
|
|
40
|
+
addJsonAndDaemonHostOptions(chat
|
|
41
|
+
.command("wait")
|
|
42
|
+
.description("Wait for new chat messages")
|
|
43
|
+
.argument("<name-or-id>", "Room name or ID")
|
|
44
|
+
.option("--timeout <duration>", "Maximum wait time")).action(withOutput(runWaitCommand));
|
|
45
|
+
return chat;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type ChatCommandOptions } from "./shared.js";
|
|
4
|
+
import { type ChatRoomRow } from "./schema.js";
|
|
5
|
+
export declare function runInspectCommand(room: string, options: ChatCommandOptions, _command: Command): Promise<SingleResult<ChatRoomRow>>;
|
|
6
|
+
//# sourceMappingURL=inspect.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { connectChatClient, toChatCommandError } from "./shared.js";
|
|
2
|
+
import { chatRoomSchema, toChatRoomRow } from "./schema.js";
|
|
3
|
+
export async function runInspectCommand(room, options, _command) {
|
|
4
|
+
const { client } = await connectChatClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const payload = await client.inspectChatRoom({ room });
|
|
7
|
+
return {
|
|
8
|
+
type: "single",
|
|
9
|
+
data: toChatRoomRow(payload.room),
|
|
10
|
+
schema: chatRoomSchema,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
throw toChatCommandError("CHAT_INSPECT_FAILED", "inspect chat room", err);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await client.close().catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { ListResult } from "../../output/index.js";
|
|
3
|
+
import { type ChatCommandOptions } from "./shared.js";
|
|
4
|
+
import { type ChatRoomRow } from "./schema.js";
|
|
5
|
+
export declare function runLsCommand(options: ChatCommandOptions, _command: Command): Promise<ListResult<ChatRoomRow>>;
|
|
6
|
+
//# sourceMappingURL=ls.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { connectChatClient, toChatCommandError } from "./shared.js";
|
|
2
|
+
import { chatRoomSchema, toChatRoomRow } from "./schema.js";
|
|
3
|
+
export async function runLsCommand(options, _command) {
|
|
4
|
+
const { client } = await connectChatClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const payload = await client.listChatRooms();
|
|
7
|
+
return {
|
|
8
|
+
type: "list",
|
|
9
|
+
data: payload.rooms.map(toChatRoomRow),
|
|
10
|
+
schema: chatRoomSchema,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
throw toChatCommandError("CHAT_LIST_FAILED", "list chat rooms", err);
|
|
15
|
+
}
|
|
16
|
+
finally {
|
|
17
|
+
await client.close().catch(() => { });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=ls.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { SingleResult } from "../../output/index.js";
|
|
3
|
+
import { type ChatCommandOptions } from "./shared.js";
|
|
4
|
+
import { type ChatMessageRow } from "./schema.js";
|
|
5
|
+
export interface ChatPostOptions extends ChatCommandOptions {
|
|
6
|
+
replyTo?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function runPostCommand(room: string, body: string, options: ChatPostOptions, _command: Command): Promise<SingleResult<ChatMessageRow>>;
|
|
9
|
+
//# sourceMappingURL=post.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { attachAgentNamesToMessages, connectChatClient, resolveChatAuthorAgentId, toChatCommandError, } from "./shared.js";
|
|
2
|
+
import { chatMessageSchema, toChatMessageRow } from "./schema.js";
|
|
3
|
+
export async function runPostCommand(room, body, options, _command) {
|
|
4
|
+
const { client } = await connectChatClient(options.host);
|
|
5
|
+
try {
|
|
6
|
+
const payload = await client.postChatMessage({
|
|
7
|
+
room,
|
|
8
|
+
body,
|
|
9
|
+
authorAgentId: resolveChatAuthorAgentId(),
|
|
10
|
+
replyToMessageId: options.replyTo,
|
|
11
|
+
});
|
|
12
|
+
const [message] = await attachAgentNamesToMessages(client, [
|
|
13
|
+
toChatMessageRow(payload.message),
|
|
14
|
+
]);
|
|
15
|
+
return {
|
|
16
|
+
type: "single",
|
|
17
|
+
data: message,
|
|
18
|
+
schema: chatMessageSchema,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
throw toChatCommandError("CHAT_POST_FAILED", "post chat message", err);
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
await client.close().catch(() => { });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=post.js.map
|