@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
package/bin/otto ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env -S node --disable-warning=DEP0040
2
+ import '../dist/index.js'
3
+
@@ -0,0 +1,19 @@
1
+ export type CliInvocation = {
2
+ kind: "cli";
3
+ argv: string[];
4
+ } | {
5
+ kind: "open-project";
6
+ resolvedPath: string;
7
+ };
8
+ export declare function isPathLikeArg(arg: string): boolean;
9
+ export declare function expandUserPath(inputPath: string): string;
10
+ export declare function isExistingDirectory(input: {
11
+ pathArg: string;
12
+ cwd: string;
13
+ }): boolean;
14
+ export declare function classifyInvocation(input: {
15
+ argv: string[];
16
+ knownCommands: ReadonlySet<string>;
17
+ cwd: string;
18
+ }): CliInvocation;
19
+ //# sourceMappingURL=classify.d.ts.map
@@ -0,0 +1,49 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import path from "node:path";
4
+ export function isPathLikeArg(arg) {
5
+ return (arg === "." ||
6
+ arg === ".." ||
7
+ arg.startsWith("./") ||
8
+ arg.startsWith("../") ||
9
+ arg.startsWith("/") ||
10
+ arg === "~" ||
11
+ arg.startsWith("~/") ||
12
+ /^[A-Za-z]:[\\/]/.test(arg));
13
+ }
14
+ export function expandUserPath(inputPath) {
15
+ if (inputPath === "~") {
16
+ return homedir();
17
+ }
18
+ if (inputPath.startsWith("~/")) {
19
+ return path.join(homedir(), inputPath.slice(2));
20
+ }
21
+ return inputPath;
22
+ }
23
+ export function isExistingDirectory(input) {
24
+ const resolvedPath = path.resolve(input.cwd, expandUserPath(input.pathArg));
25
+ if (!existsSync(resolvedPath)) {
26
+ return false;
27
+ }
28
+ return statSync(resolvedPath).isDirectory();
29
+ }
30
+ export function classifyInvocation(input) {
31
+ const [firstArg] = input.argv;
32
+ if (!firstArg) {
33
+ return { kind: "cli", argv: input.argv };
34
+ }
35
+ if (firstArg.startsWith("-")) {
36
+ return { kind: "cli", argv: input.argv };
37
+ }
38
+ if (input.knownCommands.has(firstArg)) {
39
+ return { kind: "cli", argv: input.argv };
40
+ }
41
+ if (isExistingDirectory({ pathArg: firstArg, cwd: input.cwd })) {
42
+ return {
43
+ kind: "open-project",
44
+ resolvedPath: path.resolve(input.cwd, expandUserPath(firstArg)),
45
+ };
46
+ }
47
+ return { kind: "cli", argv: input.argv };
48
+ }
49
+ //# sourceMappingURL=classify.js.map
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createCli(): Command;
3
+ //# sourceMappingURL=cli.d.ts.map
package/dist/cli.js ADDED
@@ -0,0 +1,113 @@
1
+ import { Command, Option } from "commander";
2
+ import { createAgentCommand } from "./commands/agent/index.js";
3
+ import { createDaemonCommand } from "./commands/daemon/index.js";
4
+ import { createChatCommand } from "./commands/chat/index.js";
5
+ import { createLoopCommand } from "./commands/loop/index.js";
6
+ import { createPermitCommand } from "./commands/permit/index.js";
7
+ import { createProviderCommand } from "./commands/provider/index.js";
8
+ import { createScheduleCommand } from "./commands/schedule/index.js";
9
+ import { createSpeechCommand } from "./commands/speech/index.js";
10
+ import { createTerminalCommand } from "./commands/terminal/index.js";
11
+ import { createWorktreeCommand } from "./commands/worktree/index.js";
12
+ import { createHooksCommand } from "./commands/hooks.js";
13
+ import { startCommand as daemonStartCommand } from "./commands/daemon/start.js";
14
+ import { runStatusCommand as runDaemonStatusCommand } from "./commands/daemon/status.js";
15
+ import { runRestartCommand as runDaemonRestartCommand } from "./commands/daemon/restart.js";
16
+ import { addLsOptions, runLsCommand } from "./commands/agent/ls.js";
17
+ import { addRunOptions, runRunCommand } from "./commands/agent/run.js";
18
+ import { addLogsOptions, runLogsCommand } from "./commands/agent/logs.js";
19
+ import { addDeleteOptions, runDeleteCommand } from "./commands/agent/delete.js";
20
+ import { addStopOptions, runStopCommand } from "./commands/agent/stop.js";
21
+ import { addSendOptions, runSendCommand } from "./commands/agent/send.js";
22
+ import { addInspectOptions, runInspectCommand } from "./commands/agent/inspect.js";
23
+ import { addWaitOptions, runWaitCommand } from "./commands/agent/wait.js";
24
+ import { addArchiveOptions, runArchiveCommand } from "./commands/agent/archive.js";
25
+ import { addAttachOptions, runAttachCommand } from "./commands/agent/attach.js";
26
+ import { addImportOptions, runImportCommand } from "./commands/agent/import.js";
27
+ import { withOutput } from "./output/index.js";
28
+ import { onboardCommand } from "./commands/onboard.js";
29
+ import { addDaemonHostOption, addJsonAndDaemonHostOptions, addJsonOption, } from "./utils/command-options.js";
30
+ import { resolveCliVersion } from "./version.js";
31
+ const VERSION = resolveCliVersion();
32
+ function resolveHostnamesOption(hostnames, allowedHosts) {
33
+ if (typeof hostnames === "string")
34
+ return hostnames;
35
+ if (typeof allowedHosts === "string")
36
+ return allowedHosts;
37
+ return undefined;
38
+ }
39
+ export function createCli() {
40
+ const program = new Command();
41
+ program
42
+ .name("otto")
43
+ .description("Otto CLI - control your AI coding agents from the command line")
44
+ .version(VERSION, "-v, --version", "output the version number")
45
+ // Global output options
46
+ .option("-o, --format <format>", "output format: table, json, yaml", "table")
47
+ .option("--json", "output in JSON format (alias for --format json)")
48
+ .option("-q, --quiet", "minimal output (IDs only)")
49
+ .option("--no-headers", "omit table headers")
50
+ .option("--no-color", "disable colored output");
51
+ // Primary agent commands (top-level)
52
+ addJsonAndDaemonHostOptions(addLsOptions(program.command("ls"))).action(withOutput(runLsCommand));
53
+ addJsonAndDaemonHostOptions(addRunOptions(program.command("run"))).action(withOutput(runRunCommand));
54
+ addJsonAndDaemonHostOptions(addImportOptions(program.command("import"))).action(withOutput(runImportCommand));
55
+ addDaemonHostOption(addAttachOptions(program.command("attach"))).action(runAttachCommand);
56
+ addDaemonHostOption(addLogsOptions(program.command("logs"))).action(runLogsCommand);
57
+ addJsonAndDaemonHostOptions(addStopOptions(program.command("stop"))).action(withOutput(runStopCommand));
58
+ addJsonAndDaemonHostOptions(addDeleteOptions(program.command("delete"))).action(withOutput(runDeleteCommand));
59
+ addJsonAndDaemonHostOptions(addSendOptions(program.command("send"))).action(withOutput(runSendCommand));
60
+ addJsonAndDaemonHostOptions(addInspectOptions(program.command("inspect"))).action(withOutput(runInspectCommand));
61
+ addJsonAndDaemonHostOptions(addWaitOptions(program.command("wait"))).action(withOutput(runWaitCommand));
62
+ addJsonAndDaemonHostOptions(addArchiveOptions(program.command("archive"))).action(withOutput(runArchiveCommand));
63
+ // Top-level local daemon shortcuts
64
+ program.addCommand(onboardCommand());
65
+ program.addCommand(daemonStartCommand());
66
+ program.addCommand(createHooksCommand());
67
+ addJsonOption(program
68
+ .command("status")
69
+ .description('Show local daemon status (alias for "otto daemon status")'))
70
+ .option("--home <path>", "Otto home directory (default: ~/.otto)")
71
+ .action(withOutput(runDaemonStatusCommand));
72
+ addJsonOption(program
73
+ .command("restart")
74
+ .description('Restart local daemon (alias for "otto daemon restart")'))
75
+ .option("--home <path>", "Otto home directory (default: ~/.otto)")
76
+ .option("--timeout <seconds>", "Wait timeout before force step (default: 15)")
77
+ .option("--force", "Send SIGKILL if graceful stop times out")
78
+ .option("--listen <listen>", "Listen target for restarted daemon (host:port, port, or unix socket)")
79
+ .option("--port <port>", "Port for restarted daemon listen target")
80
+ .option("--no-relay", "Disable relay on restarted daemon")
81
+ .option("--no-mcp", "Disable Agent MCP on restarted daemon")
82
+ .option("--hostnames <hosts>", 'Daemon hostnames (comma-separated, e.g. "myhost,.example.com" or "true" for any)')
83
+ .addOption(new Option("--allowed-hosts <hosts>").hideHelp())
84
+ .action(withOutput((...args) => {
85
+ const [options, command] = args.slice(-2);
86
+ return runDaemonRestartCommand({
87
+ ...options,
88
+ hostnames: resolveHostnamesOption(options.hostnames, options.allowedHosts),
89
+ }, command);
90
+ }));
91
+ // Advanced agent commands (less common operations)
92
+ program.addCommand(createAgentCommand());
93
+ // Daemon commands
94
+ program.addCommand(createDaemonCommand());
95
+ // Chat commands
96
+ program.addCommand(createChatCommand());
97
+ // Terminal commands
98
+ program.addCommand(createTerminalCommand());
99
+ // Loop commands
100
+ program.addCommand(createLoopCommand());
101
+ // Schedule commands
102
+ program.addCommand(createScheduleCommand());
103
+ // Permission commands
104
+ program.addCommand(createPermitCommand());
105
+ // Provider commands
106
+ program.addCommand(createProviderCommand());
107
+ // Speech model commands
108
+ program.addCommand(createSpeechCommand());
109
+ // Worktree commands
110
+ program.addCommand(createWorktreeCommand());
111
+ return program;
112
+ }
113
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1,18 @@
1
+ import { Command } from "commander";
2
+ import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
3
+ /** Result type for agent archive command */
4
+ export interface AgentArchiveResult {
5
+ agentId: string;
6
+ status: "archived";
7
+ archivedAt: string;
8
+ }
9
+ /** Schema for archive command output */
10
+ export declare const archiveSchema: OutputSchema<AgentArchiveResult>;
11
+ export declare function addArchiveOptions(cmd: Command): Command;
12
+ export interface AgentArchiveOptions extends CommandOptions {
13
+ force?: boolean;
14
+ host?: string;
15
+ }
16
+ export type AgentArchiveCommandResult = SingleResult<AgentArchiveResult>;
17
+ export declare function runArchiveCommand(agentIdArg: string, options: AgentArchiveOptions, _command: Command): Promise<AgentArchiveCommandResult>;
18
+ //# sourceMappingURL=archive.d.ts.map
@@ -0,0 +1,102 @@
1
+ import { connectToDaemon, getDaemonHost, resolveAgentId } from "../../utils/client.js";
2
+ /** Schema for archive command output */
3
+ export const archiveSchema = {
4
+ idField: "agentId",
5
+ columns: [
6
+ { header: "AGENT ID", field: "agentId" },
7
+ { header: "STATUS", field: "status" },
8
+ { header: "ARCHIVED AT", field: "archivedAt" },
9
+ ],
10
+ };
11
+ export function addArchiveOptions(cmd) {
12
+ return cmd
13
+ .description("Archive an agent (soft-delete)")
14
+ .argument("<id>", "Agent ID, prefix, or name")
15
+ .option("--force", "Force archive running agent (interrupts active run first)");
16
+ }
17
+ export async function runArchiveCommand(agentIdArg, options, _command) {
18
+ const host = getDaemonHost({ host: options.host });
19
+ // Validate arguments
20
+ if (!agentIdArg || agentIdArg.trim().length === 0) {
21
+ const error = {
22
+ code: "MISSING_AGENT_ID",
23
+ message: "Agent ID is required",
24
+ details: "Usage: otto agent archive <id-or-name>",
25
+ };
26
+ throw error;
27
+ }
28
+ let client;
29
+ try {
30
+ client = await connectToDaemon({ host: options.host });
31
+ }
32
+ catch (err) {
33
+ const message = err instanceof Error ? err.message : String(err);
34
+ const error = {
35
+ code: "DAEMON_NOT_RUNNING",
36
+ message: `Cannot connect to daemon at ${host}: ${message}`,
37
+ details: "Start the daemon with: otto daemon start",
38
+ };
39
+ throw error;
40
+ }
41
+ try {
42
+ const agentsPayload = await client.fetchAgents({ filter: { includeArchived: true } });
43
+ const agents = agentsPayload.entries.map((entry) => entry.agent);
44
+ const agentId = resolveAgentId(agentIdArg, agents);
45
+ if (!agentId) {
46
+ const error = {
47
+ code: "AGENT_NOT_FOUND",
48
+ message: `Agent not found: ${agentIdArg}`,
49
+ details: 'Use "otto ls" to list available agents',
50
+ };
51
+ throw error;
52
+ }
53
+ const agent = agents.find((entry) => entry.id === agentId);
54
+ if (!agent) {
55
+ throw new Error(`Resolved agent missing from fetched agents: ${agentId}`);
56
+ }
57
+ // Check if agent is already archived
58
+ if (agent.archivedAt) {
59
+ const error = {
60
+ code: "AGENT_ALREADY_ARCHIVED",
61
+ message: `Agent ${agentId.slice(0, 7)} is already archived`,
62
+ details: `Archived at: ${agent.archivedAt}`,
63
+ };
64
+ throw error;
65
+ }
66
+ // Check if agent is running and reject unless --force is set
67
+ if (agent.status === "running" && !options.force) {
68
+ const error = {
69
+ code: "AGENT_RUNNING",
70
+ message: `Agent ${agentId.slice(0, 7)} is currently running`,
71
+ details: "Use --force to archive a running agent (it will interrupt the active run), or stop it first with: otto agent stop. Use otto agent delete to hard-delete it.",
72
+ };
73
+ throw error;
74
+ }
75
+ // Archive the agent
76
+ const result = await client.archiveAgent(agentId);
77
+ await client.close();
78
+ return {
79
+ type: "single",
80
+ data: {
81
+ agentId,
82
+ status: "archived",
83
+ archivedAt: result.archivedAt,
84
+ },
85
+ schema: archiveSchema,
86
+ };
87
+ }
88
+ catch (err) {
89
+ await client.close().catch(() => { });
90
+ // Re-throw CommandError as-is
91
+ if (err && typeof err === "object" && "code" in err) {
92
+ throw err;
93
+ }
94
+ const message = err instanceof Error ? err.message : String(err);
95
+ const error = {
96
+ code: "ARCHIVE_FAILED",
97
+ message: `Failed to archive agent: ${message}`,
98
+ };
99
+ throw error;
100
+ }
101
+ }
102
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1,11 @@
1
+ import type { Command } from "commander";
2
+ export declare function addAttachOptions(cmd: Command): Command;
3
+ export interface AgentAttachOptions {
4
+ host?: string;
5
+ [key: string]: unknown;
6
+ }
7
+ /**
8
+ * Attach to a running agent's output stream
9
+ */
10
+ export declare function runAttachCommand(id: string, options: AgentAttachOptions, _command: Command): Promise<void>;
11
+ //# sourceMappingURL=attach.d.ts.map
@@ -0,0 +1,159 @@
1
+ export function addAttachOptions(cmd) {
2
+ return cmd
3
+ .description("Attach to a running agent's output stream")
4
+ .argument("<id>", "Agent ID (or prefix)");
5
+ }
6
+ import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
7
+ import { fetchProjectedTimelineItems, LIVE_HISTORY_FETCH_TIMEOUT_MS, } from "../../utils/timeline.js";
8
+ /**
9
+ * Format and print a timeline item to the terminal
10
+ */
11
+ function printTimelineItem(item) {
12
+ switch (item.type) {
13
+ case "assistant_message":
14
+ // Print assistant text directly
15
+ process.stdout.write(item.text);
16
+ break;
17
+ case "reasoning":
18
+ // Print reasoning in a muted color if available
19
+ console.log(`\n[Reasoning] ${item.text}`);
20
+ break;
21
+ case "tool_call": {
22
+ const toolName = item.name;
23
+ const status = item.status ?? "started";
24
+ console.log(`\n[Tool: ${toolName}] ${status}`);
25
+ break;
26
+ }
27
+ case "todo": {
28
+ const completed = item.items.filter((i) => i.completed).length;
29
+ const total = item.items.length;
30
+ console.log(`\n[Todo] ${completed}/${total} completed`);
31
+ break;
32
+ }
33
+ case "error":
34
+ console.error(`\n[Error] ${item.message}`);
35
+ break;
36
+ case "user_message":
37
+ console.log(`\n[User] ${item.text}`);
38
+ break;
39
+ default:
40
+ // Unknown item type, skip
41
+ break;
42
+ }
43
+ }
44
+ /**
45
+ * Format and print a stream event to the terminal
46
+ */
47
+ function printStreamEvent(event) {
48
+ switch (event.type) {
49
+ case "timeline":
50
+ // Print the timeline item
51
+ printTimelineItem(event.item);
52
+ break;
53
+ case "permission_requested":
54
+ console.log(`\n[Permission Required] ${event.request.name}`);
55
+ if (event.request.description) {
56
+ console.log(` ${event.request.description}`);
57
+ }
58
+ break;
59
+ case "permission_resolved":
60
+ console.log(`\n[Permission ${event.resolution.behavior}]`);
61
+ break;
62
+ case "turn_failed":
63
+ console.error(`\n[Turn Failed] ${event.error}`);
64
+ break;
65
+ case "attention_required":
66
+ console.log(`\n[Attention Required: ${event.reason}]`);
67
+ break;
68
+ default:
69
+ // Other event types are internal
70
+ break;
71
+ }
72
+ }
73
+ /**
74
+ * Attach to a running agent's output stream
75
+ */
76
+ export async function runAttachCommand(id, options, _command) {
77
+ const host = getDaemonHost({ host: options.host });
78
+ if (!id) {
79
+ console.error("Error: Agent ID required");
80
+ console.error("Usage: otto attach <id>");
81
+ process.exit(1);
82
+ }
83
+ let client;
84
+ try {
85
+ client = await connectToDaemon({ host: options.host });
86
+ }
87
+ catch (err) {
88
+ const message = err instanceof Error ? err.message : String(err);
89
+ console.error(`Error: Cannot connect to daemon at ${host}: ${message}`);
90
+ console.error("Start the daemon with: otto daemon start");
91
+ process.exit(1);
92
+ }
93
+ try {
94
+ const fetchResult = await client.fetchAgent({ agentId: id });
95
+ if (!fetchResult) {
96
+ console.error(`Error: No agent found matching: ${id}`);
97
+ console.error("Use `otto ls` to list available agents");
98
+ await client.close();
99
+ process.exit(1);
100
+ }
101
+ const resolvedId = fetchResult.agent.id;
102
+ // Print header
103
+ console.log(`Attaching to agent ${resolvedId.substring(0, 7)}...`);
104
+ console.log(`(Press Ctrl+C to detach)\n`);
105
+ // Print existing output from timeline fetch.
106
+ try {
107
+ const timelineItems = await fetchProjectedTimelineItems({
108
+ client,
109
+ agentId: resolvedId,
110
+ timeoutMs: LIVE_HISTORY_FETCH_TIMEOUT_MS,
111
+ });
112
+ for (const item of timelineItems) {
113
+ printTimelineItem(item);
114
+ }
115
+ }
116
+ catch (error) {
117
+ console.warn("Warning: failed to fetch existing timeline", error);
118
+ }
119
+ // Subscribe to new events
120
+ const unsubscribe = client.on("agent_stream", (msg) => {
121
+ const message = msg;
122
+ if (message.type !== "agent_stream")
123
+ return;
124
+ if (message.payload.agentId !== resolvedId)
125
+ return;
126
+ printStreamEvent(message.payload.event);
127
+ });
128
+ // Handle Ctrl+C to detach gracefully
129
+ let detached = false;
130
+ const detach = () => {
131
+ if (detached)
132
+ return;
133
+ detached = true;
134
+ console.log("\n\nDetaching from agent...");
135
+ unsubscribe();
136
+ client
137
+ .close()
138
+ .then(() => {
139
+ process.exit(0);
140
+ })
141
+ .catch(() => {
142
+ process.exit(1);
143
+ });
144
+ };
145
+ process.on("SIGINT", detach);
146
+ process.on("SIGTERM", detach);
147
+ // Keep the process alive
148
+ await new Promise(() => {
149
+ // Wait indefinitely until interrupted
150
+ });
151
+ }
152
+ catch (err) {
153
+ await client.close().catch(() => { });
154
+ const message = err instanceof Error ? err.message : String(err);
155
+ console.error(`Error: Failed to attach to agent: ${message}`);
156
+ process.exit(1);
157
+ }
158
+ }
159
+ //# sourceMappingURL=attach.js.map
@@ -0,0 +1,15 @@
1
+ import type { Command } from "commander";
2
+ export declare function addDeleteOptions(cmd: Command): Command;
3
+ import type { CommandOptions, SingleResult, OutputSchema } from "../../output/index.js";
4
+ export interface DeleteResult {
5
+ deletedCount: number;
6
+ agentIds: string[];
7
+ }
8
+ export declare const deleteSchema: OutputSchema<DeleteResult>;
9
+ export interface AgentDeleteOptions extends CommandOptions {
10
+ all?: boolean;
11
+ cwd?: string;
12
+ }
13
+ export type AgentDeleteResult = SingleResult<DeleteResult>;
14
+ export declare function runDeleteCommand(id: string | undefined, options: AgentDeleteOptions, _command: Command): Promise<AgentDeleteResult>;
15
+ //# sourceMappingURL=delete.d.ts.map
@@ -0,0 +1,107 @@
1
+ import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
2
+ import { isSameOrDescendantPath } from "../../utils/paths.js";
3
+ export function addDeleteOptions(cmd) {
4
+ return cmd
5
+ .description("Delete an agent (interrupt if running, then hard-delete)")
6
+ .argument("[id]", "Agent ID (or prefix) - optional if --all or --cwd specified")
7
+ .option("--all", "Delete all agents")
8
+ .option("--cwd <path>", "Delete all agents in directory");
9
+ }
10
+ export const deleteSchema = {
11
+ idField: (item) => item.agentIds.join("\n"),
12
+ columns: [{ header: "DELETED", field: "deletedCount" }],
13
+ };
14
+ export async function runDeleteCommand(id, options, _command) {
15
+ const host = getDaemonHost({ host: options.host });
16
+ if (!id && !options.all && !options.cwd) {
17
+ const error = {
18
+ code: "MISSING_ARGUMENT",
19
+ message: "Agent ID required unless --all or --cwd is specified",
20
+ details: "Usage: otto agent delete <id> | --all | --cwd <path>",
21
+ };
22
+ throw error;
23
+ }
24
+ let client;
25
+ try {
26
+ client = await connectToDaemon({ host: options.host });
27
+ }
28
+ catch (err) {
29
+ const message = err instanceof Error ? err.message : String(err);
30
+ const error = {
31
+ code: "DAEMON_NOT_RUNNING",
32
+ message: `Cannot connect to daemon at ${host}: ${message}`,
33
+ details: "Start the daemon with: otto daemon start",
34
+ };
35
+ throw error;
36
+ }
37
+ try {
38
+ const fetchPayload = await client.fetchAgents({ filter: { includeArchived: true } });
39
+ let agents = fetchPayload.entries.map((entry) => entry.agent);
40
+ const deletedIds = [];
41
+ if (options.all) {
42
+ agents = agents.filter((a) => !a.archivedAt);
43
+ }
44
+ else if (options.cwd) {
45
+ agents = agents.filter((a) => {
46
+ if (a.archivedAt)
47
+ return false;
48
+ return isSameOrDescendantPath(options.cwd, a.cwd);
49
+ });
50
+ }
51
+ else if (id) {
52
+ const fetchResult = await client.fetchAgent({ agentId: id });
53
+ if (!fetchResult) {
54
+ const error = {
55
+ code: "AGENT_NOT_FOUND",
56
+ message: `No agent found matching: ${id}`,
57
+ details: "Use `otto ls` to list available agents",
58
+ };
59
+ throw error;
60
+ }
61
+ agents = [fetchResult.agent];
62
+ }
63
+ const deleteResults = await Promise.all(agents.map(async (agent) => {
64
+ try {
65
+ if (agent.status === "running") {
66
+ await client.cancelAgent(agent.id);
67
+ }
68
+ await client.deleteAgent(agent.id);
69
+ return { ok: true, id: agent.id };
70
+ }
71
+ catch (err) {
72
+ const message = err instanceof Error ? err.message : String(err);
73
+ return { ok: false, id: agent.id, message };
74
+ }
75
+ }));
76
+ for (const result of deleteResults) {
77
+ if (result.ok) {
78
+ deletedIds.push(result.id);
79
+ }
80
+ else {
81
+ console.error(`Warning: Failed to delete agent ${result.id.slice(0, 7)}: ${result.message}`);
82
+ }
83
+ }
84
+ await client.close();
85
+ return {
86
+ type: "single",
87
+ data: {
88
+ deletedCount: deletedIds.length,
89
+ agentIds: deletedIds,
90
+ },
91
+ schema: deleteSchema,
92
+ };
93
+ }
94
+ catch (err) {
95
+ await client.close().catch(() => { });
96
+ if (err && typeof err === "object" && "code" in err) {
97
+ throw err;
98
+ }
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ const error = {
101
+ code: "DELETE_AGENT_FAILED",
102
+ message: `Failed to delete agent(s): ${message}`,
103
+ };
104
+ throw error;
105
+ }
106
+ }
107
+ //# sourceMappingURL=delete.js.map
@@ -0,0 +1,14 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, SingleResult } from "../../output/index.js";
3
+ import { type AgentRunResult } from "./run.js";
4
+ export declare function addImportOptions(cmd: Command): Command;
5
+ export interface AgentImportOptions extends CommandOptions {
6
+ provider?: string;
7
+ cwd?: string;
8
+ label?: string[];
9
+ host?: string;
10
+ }
11
+ export type AgentImportCommandResult = SingleResult<AgentRunResult>;
12
+ export declare function resolveImportCwd(explicitCwd: string | undefined, defaultCwd: string): string;
13
+ export declare function runImportCommand(sessionIdArg: string, options: AgentImportOptions, _command: Command): Promise<AgentImportCommandResult>;
14
+ //# sourceMappingURL=import.d.ts.map