@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.
Files changed (200) hide show
  1. package/bin/otto +3 -0
  2. package/dist/classify.d.ts +19 -0
  3. package/dist/classify.js +49 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.js +113 -0
  6. package/dist/commands/agent/archive.d.ts +18 -0
  7. package/dist/commands/agent/archive.js +102 -0
  8. package/dist/commands/agent/attach.d.ts +11 -0
  9. package/dist/commands/agent/attach.js +159 -0
  10. package/dist/commands/agent/delete.d.ts +15 -0
  11. package/dist/commands/agent/delete.js +107 -0
  12. package/dist/commands/agent/import.d.ts +14 -0
  13. package/dist/commands/agent/import.js +122 -0
  14. package/dist/commands/agent/index.d.ts +3 -0
  15. package/dist/commands/agent/index.js +48 -0
  16. package/dist/commands/agent/inspect.d.ts +15 -0
  17. package/dist/commands/agent/inspect.js +209 -0
  18. package/dist/commands/agent/logs.d.ts +19 -0
  19. package/dist/commands/agent/logs.js +180 -0
  20. package/dist/commands/agent/ls.d.ts +44 -0
  21. package/dist/commands/agent/ls.js +204 -0
  22. package/dist/commands/agent/mode.d.ts +18 -0
  23. package/dist/commands/agent/mode.js +100 -0
  24. package/dist/commands/agent/reload.d.ts +15 -0
  25. package/dist/commands/agent/reload.js +75 -0
  26. package/dist/commands/agent/run.d.ts +44 -0
  27. package/dist/commands/agent/run.js +464 -0
  28. package/dist/commands/agent/send.d.ts +19 -0
  29. package/dist/commands/agent/send.js +187 -0
  30. package/dist/commands/agent/stop.d.ts +17 -0
  31. package/dist/commands/agent/stop.js +115 -0
  32. package/dist/commands/agent/update.d.ts +18 -0
  33. package/dist/commands/agent/update.js +139 -0
  34. package/dist/commands/agent/wait.d.ts +17 -0
  35. package/dist/commands/agent/wait.js +158 -0
  36. package/dist/commands/chat/create.d.ts +9 -0
  37. package/dist/commands/chat/create.js +23 -0
  38. package/dist/commands/chat/delete.d.ts +6 -0
  39. package/dist/commands/chat/delete.js +20 -0
  40. package/dist/commands/chat/index.d.ts +3 -0
  41. package/dist/commands/chat/index.js +47 -0
  42. package/dist/commands/chat/inspect.d.ts +6 -0
  43. package/dist/commands/chat/inspect.js +20 -0
  44. package/dist/commands/chat/ls.d.ts +6 -0
  45. package/dist/commands/chat/ls.js +20 -0
  46. package/dist/commands/chat/post.d.ts +9 -0
  47. package/dist/commands/chat/post.js +28 -0
  48. package/dist/commands/chat/read.d.ts +11 -0
  49. package/dist/commands/chat/read.js +40 -0
  50. package/dist/commands/chat/schema.d.ts +36 -0
  51. package/dist/commands/chat/schema.js +81 -0
  52. package/dist/commands/chat/shared.d.ts +19 -0
  53. package/dist/commands/chat/shared.js +118 -0
  54. package/dist/commands/chat/wait.d.ts +9 -0
  55. package/dist/commands/chat/wait.js +45 -0
  56. package/dist/commands/daemon/index.d.ts +3 -0
  57. package/dist/commands/daemon/index.js +57 -0
  58. package/dist/commands/daemon/local-daemon.d.ts +78 -0
  59. package/dist/commands/daemon/local-daemon.js +544 -0
  60. package/dist/commands/daemon/pair.d.ts +9 -0
  61. package/dist/commands/daemon/pair.js +76 -0
  62. package/dist/commands/daemon/restart.d.ts +12 -0
  63. package/dist/commands/daemon/restart.js +99 -0
  64. package/dist/commands/daemon/runtime-toolchain.d.ts +6 -0
  65. package/dist/commands/daemon/runtime-toolchain.js +87 -0
  66. package/dist/commands/daemon/set-password.d.ts +17 -0
  67. package/dist/commands/daemon/set-password.js +82 -0
  68. package/dist/commands/daemon/start.d.ts +6 -0
  69. package/dist/commands/daemon/start.js +56 -0
  70. package/dist/commands/daemon/status.d.ts +10 -0
  71. package/dist/commands/daemon/status.js +338 -0
  72. package/dist/commands/daemon/stop.d.ts +15 -0
  73. package/dist/commands/daemon/stop.js +60 -0
  74. package/dist/commands/hooks.d.ts +19 -0
  75. package/dist/commands/hooks.js +89 -0
  76. package/dist/commands/loop/index.d.ts +3 -0
  77. package/dist/commands/loop/index.js +18 -0
  78. package/dist/commands/loop/inspect.d.ts +13 -0
  79. package/dist/commands/loop/inspect.js +100 -0
  80. package/dist/commands/loop/logs.d.ts +8 -0
  81. package/dist/commands/loop/logs.js +69 -0
  82. package/dist/commands/loop/ls.d.ts +18 -0
  83. package/dist/commands/loop/ls.js +62 -0
  84. package/dist/commands/loop/run.d.ts +30 -0
  85. package/dist/commands/loop/run.js +162 -0
  86. package/dist/commands/loop/stop.d.ts +15 -0
  87. package/dist/commands/loop/stop.js +56 -0
  88. package/dist/commands/loop/types.d.ts +138 -0
  89. package/dist/commands/loop/types.js +2 -0
  90. package/dist/commands/onboard.d.ts +10 -0
  91. package/dist/commands/onboard.js +416 -0
  92. package/dist/commands/open.d.ts +2 -0
  93. package/dist/commands/open.js +81 -0
  94. package/dist/commands/permit/allow.d.ts +20 -0
  95. package/dist/commands/permit/allow.js +133 -0
  96. package/dist/commands/permit/deny.d.ts +12 -0
  97. package/dist/commands/permit/deny.js +105 -0
  98. package/dist/commands/permit/index.d.ts +3 -0
  99. package/dist/commands/permit/index.js +27 -0
  100. package/dist/commands/permit/ls.d.ts +18 -0
  101. package/dist/commands/permit/ls.js +66 -0
  102. package/dist/commands/provider/index.d.ts +3 -0
  103. package/dist/commands/provider/index.js +16 -0
  104. package/dist/commands/provider/ls.d.ts +19 -0
  105. package/dist/commands/provider/ls.js +73 -0
  106. package/dist/commands/provider/models.d.ts +20 -0
  107. package/dist/commands/provider/models.js +53 -0
  108. package/dist/commands/schedule/create.d.ts +18 -0
  109. package/dist/commands/schedule/create.js +40 -0
  110. package/dist/commands/schedule/delete.d.ts +10 -0
  111. package/dist/commands/schedule/delete.js +32 -0
  112. package/dist/commands/schedule/index.d.ts +3 -0
  113. package/dist/commands/schedule/index.js +66 -0
  114. package/dist/commands/schedule/inspect.d.ts +6 -0
  115. package/dist/commands/schedule/inspect.js +24 -0
  116. package/dist/commands/schedule/logs.d.ts +6 -0
  117. package/dist/commands/schedule/logs.js +23 -0
  118. package/dist/commands/schedule/ls.d.ts +5 -0
  119. package/dist/commands/schedule/ls.js +23 -0
  120. package/dist/commands/schedule/pause.d.ts +5 -0
  121. package/dist/commands/schedule/pause.js +23 -0
  122. package/dist/commands/schedule/resume.d.ts +5 -0
  123. package/dist/commands/schedule/resume.js +23 -0
  124. package/dist/commands/schedule/run-once.d.ts +5 -0
  125. package/dist/commands/schedule/run-once.js +23 -0
  126. package/dist/commands/schedule/schema.d.ts +21 -0
  127. package/dist/commands/schedule/schema.js +67 -0
  128. package/dist/commands/schedule/shared.d.ts +56 -0
  129. package/dist/commands/schedule/shared.js +346 -0
  130. package/dist/commands/schedule/types.d.ts +168 -0
  131. package/dist/commands/schedule/types.js +2 -0
  132. package/dist/commands/schedule/update.d.ts +21 -0
  133. package/dist/commands/schedule/update.js +39 -0
  134. package/dist/commands/speech/index.d.ts +3 -0
  135. package/dist/commands/speech/index.js +5 -0
  136. package/dist/commands/terminal/capture.d.ts +10 -0
  137. package/dist/commands/terminal/capture.js +68 -0
  138. package/dist/commands/terminal/create.d.ts +10 -0
  139. package/dist/commands/terminal/create.js +38 -0
  140. package/dist/commands/terminal/index.d.ts +3 -0
  141. package/dist/commands/terminal/index.js +43 -0
  142. package/dist/commands/terminal/kill.d.ts +6 -0
  143. package/dist/commands/terminal/kill.js +36 -0
  144. package/dist/commands/terminal/ls.d.ts +10 -0
  145. package/dist/commands/terminal/ls.js +21 -0
  146. package/dist/commands/terminal/schema.d.ts +18 -0
  147. package/dist/commands/terminal/schema.js +23 -0
  148. package/dist/commands/terminal/send-keys.d.ts +7 -0
  149. package/dist/commands/terminal/send-keys.js +76 -0
  150. package/dist/commands/terminal/shared.d.ts +12 -0
  151. package/dist/commands/terminal/shared.js +61 -0
  152. package/dist/commands/worktree/archive.d.ts +20 -0
  153. package/dist/commands/worktree/archive.js +104 -0
  154. package/dist/commands/worktree/create-input.d.ts +15 -0
  155. package/dist/commands/worktree/create-input.js +56 -0
  156. package/dist/commands/worktree/create.d.ts +11 -0
  157. package/dist/commands/worktree/create.js +58 -0
  158. package/dist/commands/worktree/index.d.ts +3 -0
  159. package/dist/commands/worktree/index.js +25 -0
  160. package/dist/commands/worktree/ls.d.ts +19 -0
  161. package/dist/commands/worktree/ls.js +97 -0
  162. package/dist/index.d.ts +2 -0
  163. package/dist/index.js +6 -0
  164. package/dist/output/index.d.ts +45 -0
  165. package/dist/output/index.js +47 -0
  166. package/dist/output/json.d.ts +11 -0
  167. package/dist/output/json.js +37 -0
  168. package/dist/output/quiet.d.ts +9 -0
  169. package/dist/output/quiet.js +22 -0
  170. package/dist/output/render.d.ts +15 -0
  171. package/dist/output/render.js +83 -0
  172. package/dist/output/table.d.ts +13 -0
  173. package/dist/output/table.js +135 -0
  174. package/dist/output/types.d.ts +73 -0
  175. package/dist/output/types.js +8 -0
  176. package/dist/output/with-output.d.ts +41 -0
  177. package/dist/output/with-output.js +87 -0
  178. package/dist/output/yaml.d.ts +11 -0
  179. package/dist/output/yaml.js +38 -0
  180. package/dist/run.d.ts +14 -0
  181. package/dist/run.js +33 -0
  182. package/dist/utils/client-id.d.ts +2 -0
  183. package/dist/utils/client-id.js +37 -0
  184. package/dist/utils/client.d.ts +44 -0
  185. package/dist/utils/client.js +330 -0
  186. package/dist/utils/command-options.d.ts +6 -0
  187. package/dist/utils/command-options.js +17 -0
  188. package/dist/utils/duration.d.ts +7 -0
  189. package/dist/utils/duration.js +38 -0
  190. package/dist/utils/errors.d.ts +5 -0
  191. package/dist/utils/errors.js +10 -0
  192. package/dist/utils/paths.d.ts +12 -0
  193. package/dist/utils/paths.js +22 -0
  194. package/dist/utils/provider-model.d.ts +11 -0
  195. package/dist/utils/provider-model.js +49 -0
  196. package/dist/utils/timeline.d.ts +11 -0
  197. package/dist/utils/timeline.js +11 -0
  198. package/dist/version.d.ts +2 -0
  199. package/dist/version.js +10 -0
  200. 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,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createChatCommand(): Command;
3
+ //# sourceMappingURL=index.d.ts.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