@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,122 @@
1
+ import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
2
+ import { collectMultiple } from "../../utils/command-options.js";
3
+ import { agentRunSchema } from "./run.js";
4
+ export function addImportOptions(cmd) {
5
+ return cmd
6
+ .description("Import an existing provider session as a Otto agent")
7
+ .argument("<id>", "Provider session/thread ID to import")
8
+ .requiredOption("--provider <provider>", "Agent provider id")
9
+ .option("--cwd <path>", "Working directory for providers that require it")
10
+ .option("--label <key=value>", "Add label(s) to the agent (can be used multiple times)", collectMultiple, []);
11
+ }
12
+ function toImportResult(agent) {
13
+ return {
14
+ agentId: agent.id,
15
+ status: agent.status === "running" ? "running" : "created",
16
+ provider: agent.provider,
17
+ cwd: agent.cwd,
18
+ title: agent.title,
19
+ };
20
+ }
21
+ function parseImportProvider(provider) {
22
+ const normalizedProvider = provider?.trim();
23
+ if (!normalizedProvider) {
24
+ throw {
25
+ code: "MISSING_PROVIDER",
26
+ message: "Provider is required",
27
+ details: "Usage: otto import --provider <provider> <id>",
28
+ };
29
+ }
30
+ return normalizedProvider;
31
+ }
32
+ function parseImportLabels(labelFlags) {
33
+ const labels = {};
34
+ if (!labelFlags) {
35
+ return labels;
36
+ }
37
+ for (const labelFlag of labelFlags) {
38
+ const eqIndex = labelFlag.indexOf("=");
39
+ if (eqIndex === -1) {
40
+ throw {
41
+ code: "INVALID_LABEL",
42
+ message: `Invalid label format: ${labelFlag}`,
43
+ details: "Labels must be in key=value format",
44
+ };
45
+ }
46
+ const key = labelFlag.slice(0, eqIndex).trim();
47
+ if (!key) {
48
+ throw {
49
+ code: "INVALID_LABEL",
50
+ message: `Invalid label format: ${labelFlag}`,
51
+ details: "Labels must include a non-empty key in key=value format",
52
+ };
53
+ }
54
+ labels[key] = labelFlag.slice(eqIndex + 1);
55
+ }
56
+ return labels;
57
+ }
58
+ export function resolveImportCwd(explicitCwd, defaultCwd) {
59
+ const cwd = explicitCwd?.trim() ?? defaultCwd;
60
+ if (!cwd.trim()) {
61
+ throw {
62
+ code: "INVALID_CWD",
63
+ message: "--cwd cannot be empty",
64
+ details: "Provide a working directory path or omit --cwd",
65
+ };
66
+ }
67
+ return cwd;
68
+ }
69
+ async function connectToDaemonOrThrow(hostOption, host) {
70
+ try {
71
+ return await connectToDaemon({ host: hostOption });
72
+ }
73
+ catch (err) {
74
+ const message = err instanceof Error ? err.message : String(err);
75
+ throw {
76
+ code: "DAEMON_NOT_RUNNING",
77
+ message: `Cannot connect to daemon at ${host}: ${message}`,
78
+ details: "Start the daemon with: otto daemon start",
79
+ };
80
+ }
81
+ }
82
+ export async function runImportCommand(sessionIdArg, options, _command) {
83
+ const host = getDaemonHost({ host: options.host });
84
+ const sessionId = sessionIdArg.trim();
85
+ if (!sessionId) {
86
+ throw {
87
+ code: "MISSING_SESSION_ID",
88
+ message: "Session ID is required",
89
+ details: "Usage: otto import --provider <provider> <id>",
90
+ };
91
+ }
92
+ const provider = parseImportProvider(options.provider);
93
+ const cwd = resolveImportCwd(options.cwd, process.cwd());
94
+ const labels = parseImportLabels(options.label);
95
+ const client = await connectToDaemonOrThrow(options.host, host);
96
+ try {
97
+ const agent = await client.importAgent({
98
+ provider,
99
+ sessionId,
100
+ cwd,
101
+ ...(Object.keys(labels).length > 0 ? { labels } : {}),
102
+ });
103
+ await client.close();
104
+ return {
105
+ type: "single",
106
+ data: toImportResult(agent),
107
+ schema: agentRunSchema,
108
+ };
109
+ }
110
+ catch (err) {
111
+ await client.close().catch(() => { });
112
+ if (err && typeof err === "object" && "code" in err) {
113
+ throw err;
114
+ }
115
+ const message = err instanceof Error ? err.message : String(err);
116
+ throw {
117
+ code: "AGENT_IMPORT_FAILED",
118
+ message: `Failed to import agent: ${message}`,
119
+ };
120
+ }
121
+ }
122
+ //# sourceMappingURL=import.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createAgentCommand(): Command;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { Command } from "commander";
2
+ import { runModeCommand } from "./mode.js";
3
+ import { addArchiveOptions, runArchiveCommand } from "./archive.js";
4
+ import { addDeleteOptions, runDeleteCommand } from "./delete.js";
5
+ import { addLsOptions, runLsCommand } from "./ls.js";
6
+ import { addRunOptions, runRunCommand } from "./run.js";
7
+ import { addLogsOptions, runLogsCommand } from "./logs.js";
8
+ import { addStopOptions, runStopCommand } from "./stop.js";
9
+ import { addSendOptions, runSendCommand } from "./send.js";
10
+ import { addInspectOptions, runInspectCommand } from "./inspect.js";
11
+ import { addWaitOptions, runWaitCommand } from "./wait.js";
12
+ import { addAttachOptions, runAttachCommand } from "./attach.js";
13
+ import { addReloadOptions, runReloadCommand } from "./reload.js";
14
+ import { addImportOptions, runImportCommand } from "./import.js";
15
+ import { runUpdateCommand } from "./update.js";
16
+ import { withOutput } from "../../output/index.js";
17
+ import { addDaemonHostOption, addJsonAndDaemonHostOptions, collectMultiple, } from "../../utils/command-options.js";
18
+ export function createAgentCommand() {
19
+ const agent = new Command("agent").description("Manage agents (advanced operations)");
20
+ // Primary agent commands (same as top-level)
21
+ addJsonAndDaemonHostOptions(addLsOptions(agent.command("ls"))).action(withOutput(runLsCommand));
22
+ addJsonAndDaemonHostOptions(addRunOptions(agent.command("run"))).action(withOutput(runRunCommand));
23
+ addJsonAndDaemonHostOptions(addImportOptions(agent.command("import"))).action(withOutput(runImportCommand));
24
+ addDaemonHostOption(addAttachOptions(agent.command("attach"))).action(runAttachCommand);
25
+ addDaemonHostOption(addLogsOptions(agent.command("logs"))).action(runLogsCommand);
26
+ addJsonAndDaemonHostOptions(addStopOptions(agent.command("stop"))).action(withOutput(runStopCommand));
27
+ addJsonAndDaemonHostOptions(addDeleteOptions(agent.command("delete"))).action(withOutput(runDeleteCommand));
28
+ addJsonAndDaemonHostOptions(addSendOptions(agent.command("send"))).action(withOutput(runSendCommand));
29
+ addJsonAndDaemonHostOptions(addInspectOptions(agent.command("inspect"))).action(withOutput(runInspectCommand));
30
+ addJsonAndDaemonHostOptions(addWaitOptions(agent.command("wait"))).action(withOutput(runWaitCommand));
31
+ // Advanced agent commands (less common operations)
32
+ addJsonAndDaemonHostOptions(agent
33
+ .command("mode")
34
+ .description("Change an agent's operational mode")
35
+ .argument("<id>", "Agent ID (or prefix)")
36
+ .argument("[mode]", "Mode to set (required unless --list)")
37
+ .option("--list", "List available modes for this agent")).action(withOutput(runModeCommand));
38
+ addJsonAndDaemonHostOptions(addArchiveOptions(agent.command("archive"))).action(withOutput(runArchiveCommand));
39
+ addJsonAndDaemonHostOptions(addReloadOptions(agent.command("reload"))).action(withOutput(runReloadCommand));
40
+ addJsonAndDaemonHostOptions(agent
41
+ .command("update")
42
+ .description("Update an agent's metadata")
43
+ .argument("<id>", "Agent ID (or prefix)")
44
+ .option("--name <name>", "Update the agent's display name")
45
+ .option("--label <label>", "Add/set label(s) on the agent (can be used multiple times or comma-separated)", collectMultiple, [])).action(withOutput(runUpdateCommand));
46
+ return agent;
47
+ }
48
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,15 @@
1
+ import type { Command } from "commander";
2
+ import type { CommandOptions, ListResult } from "../../output/index.js";
3
+ export declare function addInspectOptions(cmd: Command): Command;
4
+ /** Key-value row for table display */
5
+ interface InspectRow {
6
+ key: string;
7
+ value: string;
8
+ }
9
+ export type AgentInspectResult = ListResult<InspectRow>;
10
+ export interface AgentInspectOptions extends CommandOptions {
11
+ host?: string;
12
+ }
13
+ export declare function runInspectCommand(agentIdArg: string, options: AgentInspectOptions, _command: Command): Promise<AgentInspectResult>;
14
+ export {};
15
+ //# sourceMappingURL=inspect.d.ts.map
@@ -0,0 +1,209 @@
1
+ import { PARENT_AGENT_ID_LABEL } from "@otto-code/protocol/agent-labels";
2
+ import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
3
+ export function addInspectOptions(cmd) {
4
+ return cmd
5
+ .description("Show detailed information about an agent")
6
+ .argument("<id>", "Agent ID (or prefix)");
7
+ }
8
+ /** Schema for key-value display with custom serialization for JSON/YAML */
9
+ function createInspectSchema(agent) {
10
+ return {
11
+ idField: "key",
12
+ columns: [
13
+ { header: "KEY", field: "key" },
14
+ {
15
+ header: "VALUE",
16
+ field: "value",
17
+ color: (_, item) => {
18
+ if (item.key === "Status") {
19
+ if (item.value === "running")
20
+ return "green";
21
+ if (item.value === "idle")
22
+ return "yellow";
23
+ if (item.value === "error")
24
+ return "red";
25
+ }
26
+ return undefined;
27
+ },
28
+ },
29
+ ],
30
+ // For JSON/YAML, return the structured agent object
31
+ serialize: (_item) => agent,
32
+ };
33
+ }
34
+ /** Shorten home directory in path */
35
+ function shortenPath(path) {
36
+ const home = process.env.HOME;
37
+ if (home && path.startsWith(home)) {
38
+ return "~" + path.slice(home.length);
39
+ }
40
+ return path;
41
+ }
42
+ /** Format cost in USD */
43
+ function formatCost(costUsd) {
44
+ if (costUsd === 0)
45
+ return "$0.00";
46
+ if (costUsd < 0.01)
47
+ return `$${costUsd.toFixed(4)}`;
48
+ return `$${costUsd.toFixed(2)}`;
49
+ }
50
+ function normalizeModelId(value) {
51
+ if (typeof value !== "string")
52
+ return null;
53
+ const normalized = value.trim();
54
+ if (!normalized || normalized.toLowerCase() === "default")
55
+ return null;
56
+ return normalized;
57
+ }
58
+ function resolveModel(snapshot) {
59
+ return normalizeModelId(snapshot.runtimeInfo?.model) ?? normalizeModelId(snapshot.model);
60
+ }
61
+ function buildLastUsage(snapshot) {
62
+ if (!snapshot.lastUsage)
63
+ return null;
64
+ return {
65
+ InputTokens: snapshot.lastUsage.inputTokens ?? 0,
66
+ OutputTokens: snapshot.lastUsage.outputTokens ?? 0,
67
+ CachedTokens: snapshot.lastUsage.cachedInputTokens ?? 0,
68
+ CostUsd: snapshot.lastUsage.totalCostUsd ?? 0,
69
+ };
70
+ }
71
+ function buildCapabilities(snapshot) {
72
+ if (!snapshot.capabilities)
73
+ return null;
74
+ return {
75
+ Streaming: snapshot.capabilities.supportsStreaming ?? false,
76
+ Persistence: snapshot.capabilities.supportsSessionPersistence ?? false,
77
+ DynamicModes: snapshot.capabilities.supportsDynamicModes ?? false,
78
+ McpServers: snapshot.capabilities.supportsMcpServers ?? false,
79
+ };
80
+ }
81
+ /** Convert agent snapshot to inspection data */
82
+ function toInspectData(snapshot) {
83
+ return {
84
+ Id: snapshot.id,
85
+ Name: snapshot.title ?? "-",
86
+ Provider: snapshot.provider,
87
+ Model: resolveModel(snapshot) ?? "-",
88
+ Thinking: snapshot.effectiveThinkingOptionId ?? "auto",
89
+ Status: snapshot.status,
90
+ Archived: snapshot.archivedAt != null,
91
+ ArchivedAt: snapshot.archivedAt ?? null,
92
+ Mode: snapshot.currentModeId ?? "default",
93
+ Cwd: snapshot.cwd,
94
+ CreatedAt: snapshot.createdAt,
95
+ UpdatedAt: snapshot.updatedAt,
96
+ LastUsage: buildLastUsage(snapshot),
97
+ Capabilities: buildCapabilities(snapshot),
98
+ AvailableModes: snapshot.availableModes
99
+ ? snapshot.availableModes.map((m) => ({ id: m.id, label: m.label }))
100
+ : null,
101
+ PendingPermissions: (snapshot.pendingPermissions ?? []).map((p) => ({
102
+ id: p.id,
103
+ tool: p.name ?? "unknown",
104
+ })),
105
+ Worktree: snapshot.labels?.["otto.worktree"] ?? null,
106
+ ParentAgentId: snapshot.labels?.[PARENT_AGENT_ID_LABEL] ?? null,
107
+ };
108
+ }
109
+ /** Convert agent to key-value rows for table display */
110
+ function toInspectRows(agent) {
111
+ const rows = [
112
+ { key: "Id", value: agent.Id },
113
+ { key: "Name", value: agent.Name },
114
+ { key: "Provider", value: agent.Provider },
115
+ { key: "Model", value: agent.Model },
116
+ { key: "Thinking", value: agent.Thinking },
117
+ { key: "Status", value: agent.Status },
118
+ { key: "Archived", value: String(agent.Archived) },
119
+ { key: "ArchivedAt", value: agent.ArchivedAt ?? "null" },
120
+ { key: "Mode", value: agent.Mode },
121
+ { key: "Cwd", value: shortenPath(agent.Cwd) },
122
+ { key: "CreatedAt", value: agent.CreatedAt },
123
+ { key: "UpdatedAt", value: agent.UpdatedAt },
124
+ ];
125
+ if (agent.LastUsage) {
126
+ rows.push({
127
+ key: "LastUsage",
128
+ value: `InputTokens: ${agent.LastUsage.InputTokens}, OutputTokens: ${agent.LastUsage.OutputTokens}, CachedTokens: ${agent.LastUsage.CachedTokens}, CostUsd: ${formatCost(agent.LastUsage.CostUsd)}`,
129
+ });
130
+ }
131
+ if (agent.Capabilities) {
132
+ rows.push({
133
+ key: "Capabilities",
134
+ value: `Streaming: ${agent.Capabilities.Streaming}, Persistence: ${agent.Capabilities.Persistence}, DynamicModes: ${agent.Capabilities.DynamicModes}, McpServers: ${agent.Capabilities.McpServers}`,
135
+ });
136
+ }
137
+ if (agent.AvailableModes && agent.AvailableModes.length > 0) {
138
+ rows.push({
139
+ key: "AvailableModes",
140
+ value: agent.AvailableModes.map((m) => `${m.id} (${m.label})`).join(", "),
141
+ });
142
+ }
143
+ rows.push({
144
+ key: "PendingPermissions",
145
+ value: agent.PendingPermissions.length > 0
146
+ ? agent.PendingPermissions.map((p) => `${p.id} (${p.tool})`).join(", ")
147
+ : "[]",
148
+ });
149
+ rows.push({ key: "Worktree", value: agent.Worktree ?? "null" });
150
+ rows.push({ key: "ParentAgentId", value: agent.ParentAgentId ?? "null" });
151
+ return rows;
152
+ }
153
+ export async function runInspectCommand(agentIdArg, options, _command) {
154
+ const host = getDaemonHost({ host: options.host });
155
+ // Validate arguments
156
+ if (!agentIdArg || agentIdArg.trim().length === 0) {
157
+ const error = {
158
+ code: "MISSING_AGENT_ID",
159
+ message: "Agent ID is required",
160
+ details: "Usage: otto agent inspect <id>",
161
+ };
162
+ throw error;
163
+ }
164
+ let client;
165
+ try {
166
+ client = await connectToDaemon({ host: options.host });
167
+ }
168
+ catch (err) {
169
+ const message = err instanceof Error ? err.message : String(err);
170
+ const error = {
171
+ code: "DAEMON_NOT_RUNNING",
172
+ message: `Cannot connect to daemon at ${host}: ${message}`,
173
+ details: "Start the daemon with: otto daemon start",
174
+ };
175
+ throw error;
176
+ }
177
+ try {
178
+ const fetchResult = await client.fetchAgent({ agentId: agentIdArg });
179
+ if (!fetchResult) {
180
+ const error = {
181
+ code: "AGENT_NOT_FOUND",
182
+ message: `Agent not found: ${agentIdArg}`,
183
+ details: 'Use "otto ls" to list available agents',
184
+ };
185
+ throw error;
186
+ }
187
+ await client.close();
188
+ const inspectData = toInspectData(fetchResult.agent);
189
+ return {
190
+ type: "list",
191
+ data: toInspectRows(inspectData),
192
+ schema: createInspectSchema(inspectData),
193
+ };
194
+ }
195
+ catch (err) {
196
+ await client.close().catch(() => { });
197
+ // Re-throw CommandError as-is
198
+ if (err && typeof err === "object" && "code" in err) {
199
+ throw err;
200
+ }
201
+ const message = err instanceof Error ? err.message : String(err);
202
+ const error = {
203
+ code: "INSPECT_FAILED",
204
+ message: `Failed to inspect agent: ${message}`,
205
+ };
206
+ throw error;
207
+ }
208
+ }
209
+ //# sourceMappingURL=inspect.js.map
@@ -0,0 +1,19 @@
1
+ import { Command } from "commander";
2
+ import type { CommandOptions } from "../../output/index.js";
3
+ import type { DaemonClient } from "@otto-code/client/internal/daemon-client";
4
+ import type { AgentTimelineItem } from "@otto-code/protocol/agent-types";
5
+ export declare function addLogsOptions(cmd: Command): Command;
6
+ export interface AgentLogsOptions extends CommandOptions {
7
+ follow?: boolean;
8
+ tail?: string;
9
+ filter?: string;
10
+ since?: string;
11
+ }
12
+ export type AgentLogsResult = void;
13
+ export declare const NO_ACTIVITY_MESSAGE = "No activity to display.";
14
+ export declare function fetchAgentTimelineItems(client: DaemonClient, agentId: string, options?: {
15
+ timeoutMs?: number;
16
+ }): Promise<AgentTimelineItem[]>;
17
+ export declare function formatAgentActivityTranscript(timelineItems: AgentTimelineItem[], tailCount?: number): string;
18
+ export declare function runLogsCommand(id: string, options: AgentLogsOptions, _command: Command): Promise<AgentLogsResult>;
19
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1,180 @@
1
+ import { connectToDaemon, getDaemonHost } from "../../utils/client.js";
2
+ import { fetchProjectedTimelineItems, LIVE_HISTORY_FETCH_TIMEOUT_MS, } from "../../utils/timeline.js";
3
+ import { curateAgentActivity } from "@otto-code/server";
4
+ export function addLogsOptions(cmd) {
5
+ return cmd
6
+ .description("View agent activity/timeline")
7
+ .argument("<id>", "Agent ID (or prefix)")
8
+ .option("-f, --follow", "Follow log output (streaming)")
9
+ .option("--tail <n>", "Show last n entries")
10
+ .option("--filter <type>", "Filter by event type (tools, text, errors, permissions)")
11
+ .option("--since <time>", "Show logs since timestamp");
12
+ }
13
+ export const NO_ACTIVITY_MESSAGE = "No activity to display.";
14
+ export async function fetchAgentTimelineItems(client, agentId, options) {
15
+ return fetchProjectedTimelineItems({ client, agentId, timeoutMs: options?.timeoutMs });
16
+ }
17
+ export function formatAgentActivityTranscript(timelineItems, tailCount) {
18
+ if (tailCount === 0) {
19
+ return "";
20
+ }
21
+ return curateAgentActivity(timelineItems, tailCount !== undefined ? { maxItems: tailCount } : undefined);
22
+ }
23
+ function parseTailCount(raw) {
24
+ if (raw === undefined)
25
+ return undefined;
26
+ const parsed = Number.parseInt(raw, 10);
27
+ if (Number.isNaN(parsed) || parsed < 0) {
28
+ return undefined;
29
+ }
30
+ return parsed;
31
+ }
32
+ /**
33
+ * Check if a timeline item matches the filter type
34
+ */
35
+ function matchesFilter(item, filter) {
36
+ if (!filter)
37
+ return true;
38
+ const filterLower = filter.toLowerCase();
39
+ const type = item.type.toLowerCase();
40
+ switch (filterLower) {
41
+ case "tools":
42
+ return type === "tool_call";
43
+ case "text":
44
+ return type === "user_message" || type === "assistant_message" || type === "reasoning";
45
+ case "errors":
46
+ return type === "error";
47
+ case "permissions":
48
+ // Permissions might be in tool_call status or a separate event type
49
+ return type.includes("permission");
50
+ default:
51
+ // If filter doesn't match predefined types, match against the actual type
52
+ return type.includes(filterLower);
53
+ }
54
+ }
55
+ export async function runLogsCommand(id, options, _command) {
56
+ const host = getDaemonHost({ host: options.host });
57
+ if (!id) {
58
+ console.error("Error: Agent ID required");
59
+ console.error("Usage: otto agent logs <id>");
60
+ process.exit(1);
61
+ }
62
+ let client;
63
+ try {
64
+ client = await connectToDaemon({ host: options.host });
65
+ }
66
+ catch (err) {
67
+ const message = err instanceof Error ? err.message : String(err);
68
+ console.error(`Error: Cannot connect to daemon at ${host}: ${message}`);
69
+ console.error("Start the daemon with: otto daemon start");
70
+ process.exit(1);
71
+ }
72
+ try {
73
+ const fetchResult = await client.fetchAgent({ agentId: id });
74
+ if (!fetchResult) {
75
+ console.error(`Error: No agent found matching: ${id}`);
76
+ console.error("Use `otto ls` to list available agents");
77
+ await client.close();
78
+ process.exit(1);
79
+ }
80
+ const resolvedId = fetchResult.agent.id;
81
+ // For follow mode, we stream events continuously
82
+ if (options.follow) {
83
+ if (options.tail !== undefined && parseTailCount(options.tail) === undefined) {
84
+ console.error(`Error: Invalid --tail value: ${options.tail}`);
85
+ console.error("Usage: --tail <n> (where n is >= 0)");
86
+ await client.close().catch(() => { });
87
+ process.exit(1);
88
+ }
89
+ await runFollowMode(client, resolvedId, options);
90
+ return;
91
+ }
92
+ // Fetch timeline directly via cursor RPC.
93
+ let timelineItems = await fetchAgentTimelineItems(client, resolvedId);
94
+ // Apply filter
95
+ if (options.filter) {
96
+ timelineItems = timelineItems.filter((item) => matchesFilter(item, options.filter));
97
+ }
98
+ const tailCount = parseTailCount(options.tail);
99
+ if (options.tail !== undefined && tailCount === undefined) {
100
+ console.error(`Error: Invalid --tail value: ${options.tail}`);
101
+ console.error("Usage: --tail <n> (where n is >= 0)");
102
+ await client.close().catch(() => { });
103
+ process.exit(1);
104
+ }
105
+ await client.close();
106
+ // Use curateAgentActivity to format the transcript
107
+ if (tailCount === 0) {
108
+ return;
109
+ }
110
+ const transcript = formatAgentActivityTranscript(timelineItems, tailCount);
111
+ console.log(transcript);
112
+ }
113
+ catch (err) {
114
+ const message = err instanceof Error ? err.message : String(err);
115
+ console.error(`Error: Failed to get logs: ${message}`);
116
+ await client.close().catch(() => { });
117
+ process.exit(1);
118
+ }
119
+ }
120
+ /**
121
+ * Follow mode: stream logs in real-time until interrupted
122
+ */
123
+ async function runFollowMode(client, agentId, options) {
124
+ const DEFAULT_FOLLOW_TAIL = 10;
125
+ const tailCount = parseTailCount(options.tail) ?? DEFAULT_FOLLOW_TAIL;
126
+ // First, get existing timeline.
127
+ let existingItems = [];
128
+ try {
129
+ existingItems = await fetchAgentTimelineItems(client, agentId, {
130
+ timeoutMs: LIVE_HISTORY_FETCH_TIMEOUT_MS,
131
+ });
132
+ }
133
+ catch (error) {
134
+ console.warn("Warning: failed to fetch existing timeline", error);
135
+ }
136
+ // Apply filter to existing items
137
+ if (options.filter) {
138
+ existingItems = existingItems.filter((item) => matchesFilter(item, options.filter));
139
+ }
140
+ // Print existing transcript (tail-like behavior)
141
+ if (tailCount > 0) {
142
+ const existingTranscript = formatAgentActivityTranscript(existingItems, tailCount);
143
+ if (existingTranscript !== NO_ACTIVITY_MESSAGE) {
144
+ console.log(existingTranscript);
145
+ }
146
+ }
147
+ // Subscribe to new events
148
+ const tailLabel = tailCount === 0 ? "no history" : `last ${tailCount} entr${tailCount === 1 ? "y" : "ies"}`;
149
+ console.log(`\n--- Following logs (${tailLabel}; Ctrl+C to stop) ---\n`);
150
+ const unsubscribe = client.on("agent_stream", (msg) => {
151
+ const message = msg;
152
+ if (message.type !== "agent_stream")
153
+ return;
154
+ if (message.payload.agentId !== agentId)
155
+ return;
156
+ if (message.payload.event.type === "timeline") {
157
+ const item = message.payload.event.item;
158
+ // Apply filter
159
+ if (options.filter && !matchesFilter(item, options.filter)) {
160
+ return;
161
+ }
162
+ // Print each timeline item as it arrives using the curator format
163
+ const transcript = formatAgentActivityTranscript([item]);
164
+ if (transcript !== NO_ACTIVITY_MESSAGE) {
165
+ console.log(transcript);
166
+ }
167
+ }
168
+ });
169
+ // Wait for interrupt
170
+ await new Promise((resolve) => {
171
+ const cleanup = () => {
172
+ unsubscribe();
173
+ resolve();
174
+ };
175
+ process.on("SIGINT", cleanup);
176
+ process.on("SIGTERM", cleanup);
177
+ });
178
+ await client.close();
179
+ }
180
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1,44 @@
1
+ import type { Command } from "commander";
2
+ import { connectToDaemon } from "../../utils/client.js";
3
+ import type { CommandOptions, ListResult, OutputSchema } from "../../output/index.js";
4
+ type FetchAgentsOptions = NonNullable<Parameters<Awaited<ReturnType<typeof connectToDaemon>>["fetchAgents"]>[0]>;
5
+ export declare function addLsOptions(cmd: Command): Command;
6
+ /** Agent list item for display */
7
+ export interface AgentListItem {
8
+ id: string;
9
+ shortId: string;
10
+ name: string;
11
+ provider: string;
12
+ thinking: string;
13
+ status: string;
14
+ cwd: string;
15
+ created: string;
16
+ }
17
+ /** Schema for agent ls output */
18
+ export declare const agentLsSchema: OutputSchema<AgentListItem>;
19
+ export type AgentLsResult = ListResult<AgentListItem>;
20
+ export interface AgentLsOptions extends CommandOptions {
21
+ /** -a: Include archived agents */
22
+ all?: boolean;
23
+ /** -g: List agents across all directories */
24
+ global?: boolean;
25
+ /** Filter by specific status */
26
+ status?: string;
27
+ /** Filter by specific cwd */
28
+ cwd?: string;
29
+ /** Filter by labels (key=value format) */
30
+ label?: string[];
31
+ /** Filter by thinking option ID */
32
+ thinking?: string;
33
+ }
34
+ export declare function buildAgentLsFetchOptions(options: Pick<AgentLsOptions, "all" | "global" | "label" | "thinking">): FetchAgentsOptions;
35
+ /**
36
+ * Agent ls command semantics:
37
+ * - `otto agent ls` → active non-archived agents
38
+ * - `otto agent ls -g` → global non-archived agents
39
+ * - `otto agent ls -a` → active agents, including archived
40
+ * - `otto agent ls -ag` → global agents, including archived
41
+ */
42
+ export declare function runLsCommand(options: AgentLsOptions, _command: Command): Promise<AgentLsResult>;
43
+ export {};
44
+ //# sourceMappingURL=ls.d.ts.map