@agentick/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +353 -0
  3. package/dist/bin.d.ts +6 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +44 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/chat-session.d.ts +37 -0
  8. package/dist/chat-session.d.ts.map +1 -0
  9. package/dist/chat-session.js +201 -0
  10. package/dist/chat-session.js.map +1 -0
  11. package/dist/cli.d.ts +84 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +147 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/commands/chat.d.ts +13 -0
  16. package/dist/commands/chat.d.ts.map +1 -0
  17. package/dist/commands/chat.js +22 -0
  18. package/dist/commands/chat.js.map +1 -0
  19. package/dist/commands/send.d.ts +14 -0
  20. package/dist/commands/send.d.ts.map +1 -0
  21. package/dist/commands/send.js +77 -0
  22. package/dist/commands/send.js.map +1 -0
  23. package/dist/commands/status.d.ts +11 -0
  24. package/dist/commands/status.d.ts.map +1 -0
  25. package/dist/commands/status.js +22 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/config.d.ts +36 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +83 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +12 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/ui/renderer.d.ts +85 -0
  36. package/dist/ui/renderer.d.ts.map +1 -0
  37. package/dist/ui/renderer.js +163 -0
  38. package/dist/ui/renderer.js.map +1 -0
  39. package/package.json +47 -0
  40. package/src/bin.ts +50 -0
  41. package/src/chat-session.ts +244 -0
  42. package/src/cli.ts +206 -0
  43. package/src/commands/chat.ts +34 -0
  44. package/src/commands/send.ts +95 -0
  45. package/src/commands/status.ts +32 -0
  46. package/src/config.ts +123 -0
  47. package/src/index.ts +12 -0
  48. package/src/types.d.ts +33 -0
  49. package/src/ui/renderer.ts +194 -0
package/src/cli.ts ADDED
@@ -0,0 +1,206 @@
1
+ /**
2
+ * CLI - Main CLI class
3
+ */
4
+
5
+ import { createClient, type AgentickClient, type AgentickClientConfig } from "@agentick/client";
6
+ import EventSource from "eventsource";
7
+ import { EventEmitter } from "events";
8
+
9
+ /**
10
+ * CLI configuration
11
+ */
12
+ export interface CLIConfig {
13
+ /** Server URL */
14
+ url: string;
15
+
16
+ /** Session ID (optional - server will create one if not provided) */
17
+ sessionId?: string;
18
+
19
+ /** Authentication token */
20
+ token?: string;
21
+
22
+ /** Enable streaming (default: true) */
23
+ streaming?: boolean;
24
+
25
+ /** Enable debug mode */
26
+ debug?: boolean;
27
+
28
+ /** Custom headers */
29
+ headers?: Record<string, string>;
30
+ }
31
+
32
+ /**
33
+ * CLI event types
34
+ */
35
+ export interface CLIEvents {
36
+ connected: [];
37
+ disconnected: [];
38
+ error: [Error];
39
+ "message:sent": [{ content: string }];
40
+ "message:received": [{ content: string; tokens?: { input: number; output: number } }];
41
+ "stream:delta": [{ text: string }];
42
+ "stream:start": [];
43
+ "stream:end": [];
44
+ "tool:start": [{ name: string; args: Record<string, unknown> }];
45
+ "tool:end": [{ name: string; result: unknown }];
46
+ }
47
+
48
+ /**
49
+ * CLI class for programmatic usage
50
+ */
51
+ export class CLI extends EventEmitter {
52
+ private client: AgentickClient;
53
+ private config: CLIConfig;
54
+ private _sessionId?: string;
55
+ private _isConnected = false;
56
+
57
+ constructor(config: CLIConfig) {
58
+ super();
59
+ this.config = config;
60
+
61
+ // Create client with Node.js EventSource
62
+ const clientConfig: AgentickClientConfig = {
63
+ baseUrl: config.url,
64
+ token: config.token,
65
+ headers: config.headers,
66
+ // @ts-expect-error - EventSource types differ slightly
67
+ EventSource: EventSource,
68
+ };
69
+
70
+ this.client = createClient(clientConfig);
71
+ this._sessionId = config.sessionId;
72
+
73
+ // Forward connection state changes
74
+ this.client.onConnectionChange((state) => {
75
+ if (state === "connected" && !this._isConnected) {
76
+ this._isConnected = true;
77
+ this.emit("connected");
78
+ } else if (state === "disconnected" && this._isConnected) {
79
+ this._isConnected = false;
80
+ this.emit("disconnected");
81
+ } else if (state === "error") {
82
+ this.emit("error", new Error("Connection error"));
83
+ }
84
+ });
85
+ }
86
+
87
+ get sessionId(): string | undefined {
88
+ return this._sessionId;
89
+ }
90
+
91
+ get isConnected(): boolean {
92
+ return this._isConnected;
93
+ }
94
+
95
+ /**
96
+ * Send a message and get the response
97
+ */
98
+ async send(message: string): Promise<string> {
99
+ const handle = this.client.send(message, {
100
+ sessionId: this._sessionId,
101
+ });
102
+
103
+ // Track session ID from response
104
+ let response = "";
105
+
106
+ this.emit("stream:start");
107
+
108
+ for await (const event of handle) {
109
+ // Update session ID if we get one
110
+ if ("sessionId" in event && event.sessionId) {
111
+ this._sessionId = event.sessionId;
112
+ }
113
+
114
+ switch (event.type) {
115
+ case "content_delta":
116
+ if ("delta" in event) {
117
+ const delta = (event as { delta: string }).delta;
118
+ response += delta;
119
+ this.emit("stream:delta", { text: delta });
120
+ }
121
+ break;
122
+
123
+ case "tool_call_start":
124
+ if ("name" in event) {
125
+ const toolStart = event as unknown as { name: string; input?: Record<string, unknown> };
126
+ this.emit("tool:start", {
127
+ name: toolStart.name,
128
+ args: toolStart.input ?? {},
129
+ });
130
+ }
131
+ break;
132
+
133
+ case "tool_result":
134
+ if ("name" in event) {
135
+ const toolResult = event as unknown as { name: string; result?: unknown };
136
+ this.emit("tool:end", {
137
+ name: toolResult.name,
138
+ result: toolResult.result,
139
+ });
140
+ }
141
+ break;
142
+ }
143
+ }
144
+
145
+ this.emit("stream:end");
146
+
147
+ // Get final result
148
+ try {
149
+ const result = await handle.result;
150
+ if (result?.response) {
151
+ response =
152
+ typeof result.response === "string" ? result.response : JSON.stringify(result.response);
153
+ }
154
+
155
+ this.emit("message:received", {
156
+ content: response,
157
+ tokens: result?.usage
158
+ ? {
159
+ input: result.usage.inputTokens ?? 0,
160
+ output: result.usage.outputTokens ?? 0,
161
+ }
162
+ : undefined,
163
+ });
164
+ } catch (error) {
165
+ // Result might fail if we already consumed the stream
166
+ if (!response) {
167
+ throw error;
168
+ }
169
+ }
170
+
171
+ return response;
172
+ }
173
+
174
+ /**
175
+ * Send a message and stream the response
176
+ */
177
+ async *stream(message: string): AsyncGenerator<{ type: string; data: unknown }> {
178
+ const handle = this.client.send(message, {
179
+ sessionId: this._sessionId,
180
+ });
181
+
182
+ for await (const event of handle) {
183
+ // Update session ID
184
+ if ("sessionId" in event && event.sessionId) {
185
+ this._sessionId = event.sessionId;
186
+ }
187
+
188
+ yield { type: event.type, data: event };
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Destroy the client
194
+ */
195
+ destroy(): void {
196
+ this.client.destroy();
197
+ this._isConnected = false;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Create a CLI instance
203
+ */
204
+ export function createCLI(config: CLIConfig): CLI {
205
+ return new CLI(config);
206
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Chat command - Interactive chat mode
3
+ */
4
+
5
+ import { ChatSession } from "../chat-session.js";
6
+ import { loadConfig } from "../config.js";
7
+
8
+ interface ChatOptions {
9
+ url?: string;
10
+ session?: string;
11
+ token?: string;
12
+ stream?: boolean;
13
+ debug?: boolean;
14
+ }
15
+
16
+ export async function chatCommand(options: ChatOptions): Promise<void> {
17
+ // Load config with CLI overrides
18
+ const config = loadConfig(options);
19
+
20
+ if (!config.url) {
21
+ console.error("Error: Server URL is required. Use --url or set TENTICKLE_URL.");
22
+ process.exit(1);
23
+ }
24
+
25
+ const session = new ChatSession({
26
+ url: config.url,
27
+ sessionId: config.sessionId,
28
+ token: config.token,
29
+ streaming: options.stream !== false,
30
+ debug: options.debug,
31
+ });
32
+
33
+ await session.start();
34
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Send command - Non-interactive message sending
3
+ */
4
+
5
+ import * as fs from "fs";
6
+ import { CLI } from "../cli.js";
7
+ import { Renderer } from "../ui/renderer.js";
8
+ import { loadConfig } from "../config.js";
9
+
10
+ interface SendOptions {
11
+ url?: string;
12
+ session?: string;
13
+ token?: string;
14
+ stdin?: boolean;
15
+ format?: "plain" | "json" | "markdown";
16
+ stream?: boolean;
17
+ }
18
+
19
+ export async function sendCommand(message: string, options: SendOptions): Promise<void> {
20
+ const config = loadConfig(options);
21
+
22
+ if (!config.url) {
23
+ console.error("Error: Server URL is required. Use --url or set TENTICKLE_URL.");
24
+ process.exit(1);
25
+ }
26
+
27
+ const renderer = new Renderer({
28
+ markdown: options.format === "markdown",
29
+ });
30
+
31
+ // Read stdin if requested
32
+ let fullMessage = message;
33
+ if (options.stdin) {
34
+ const stdinData = fs.readFileSync(0, "utf-8");
35
+ fullMessage = `${message}\n\n${stdinData}`;
36
+ }
37
+
38
+ const cli = new CLI({
39
+ url: config.url,
40
+ sessionId: config.sessionId,
41
+ token: config.token,
42
+ streaming: options.stream !== false,
43
+ });
44
+
45
+ try {
46
+ let response = "";
47
+
48
+ if (options.stream !== false) {
49
+ // Stream mode - show output as it arrives
50
+ cli.on("stream:delta", ({ text }) => {
51
+ process.stdout.write(text);
52
+ });
53
+
54
+ cli.on("tool:start", ({ name }) => {
55
+ if (options.format !== "json") {
56
+ process.stderr.write(`\n[tool: ${name}]\n`);
57
+ }
58
+ });
59
+
60
+ response = await cli.send(fullMessage);
61
+ console.log(); // Newline after streaming
62
+ } else {
63
+ // Non-stream mode - wait for complete response
64
+ response = await cli.send(fullMessage);
65
+ }
66
+
67
+ // Output based on format
68
+ switch (options.format) {
69
+ case "json":
70
+ renderer.json({
71
+ response,
72
+ sessionId: cli.sessionId,
73
+ });
74
+ break;
75
+ case "markdown":
76
+ if (options.stream === false) {
77
+ renderer.response(response);
78
+ }
79
+ break;
80
+ case "plain":
81
+ default:
82
+ if (options.stream === false) {
83
+ renderer.plain(response);
84
+ }
85
+ break;
86
+ }
87
+
88
+ cli.destroy();
89
+ process.exit(0);
90
+ } catch (error) {
91
+ renderer.error(error instanceof Error ? error.message : String(error));
92
+ cli.destroy();
93
+ process.exit(1);
94
+ }
95
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Status command - Show server and session status
3
+ */
4
+
5
+ import { Renderer } from "../ui/renderer.js";
6
+ import { loadConfig } from "../config.js";
7
+
8
+ interface StatusOptions {
9
+ url?: string;
10
+ session?: string;
11
+ token?: string;
12
+ }
13
+
14
+ export async function statusCommand(options: StatusOptions): Promise<void> {
15
+ const config = loadConfig(options);
16
+ const renderer = new Renderer();
17
+
18
+ if (!config.url) {
19
+ console.error("Error: Server URL is required. Use --url or set TENTICKLE_URL.");
20
+ process.exit(1);
21
+ }
22
+
23
+ renderer.info("\nAgentick Status");
24
+ renderer.separator();
25
+ renderer.info(`URL: ${config.url}`);
26
+ renderer.info(`Session: ${config.sessionId ?? "(auto-create)"}`);
27
+ renderer.info(`Token: ${config.token ? "(set)" : "(not set)"}`);
28
+ renderer.separator();
29
+
30
+ renderer.info("\nNote: Server status check not yet implemented.");
31
+ renderer.info("Use 'agentick chat' to connect and '/status' for session info.\n");
32
+ }
package/src/config.ts ADDED
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Configuration loading and management
3
+ */
4
+
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import * as os from "os";
8
+
9
+ /**
10
+ * CLI configuration
11
+ */
12
+ export interface Config {
13
+ url?: string;
14
+ sessionId?: string;
15
+ token?: string;
16
+ debug?: boolean;
17
+ }
18
+
19
+ /**
20
+ * Config file structure
21
+ */
22
+ interface ConfigFile {
23
+ defaultUrl?: string;
24
+ defaultToken?: string;
25
+ aliases?: Record<string, string>;
26
+ debug?: boolean;
27
+ }
28
+
29
+ /**
30
+ * Get config file path
31
+ */
32
+ function getConfigPath(): string {
33
+ return path.join(os.homedir(), ".agentick", "config.json");
34
+ }
35
+
36
+ /**
37
+ * Load config file
38
+ */
39
+ function loadConfigFile(): ConfigFile | null {
40
+ const configPath = getConfigPath();
41
+
42
+ try {
43
+ if (fs.existsSync(configPath)) {
44
+ const content = fs.readFileSync(configPath, "utf-8");
45
+ return JSON.parse(content) as ConfigFile;
46
+ }
47
+ } catch {
48
+ // Ignore config file errors
49
+ }
50
+
51
+ return null;
52
+ }
53
+
54
+ /**
55
+ * Resolve URL alias
56
+ */
57
+ function resolveAlias(url: string, configFile: ConfigFile | null): string {
58
+ if (configFile?.aliases && configFile.aliases[url]) {
59
+ return configFile.aliases[url];
60
+ }
61
+ return url;
62
+ }
63
+
64
+ /**
65
+ * Load configuration from environment, config file, and CLI options
66
+ */
67
+ export function loadConfig(cliOptions: {
68
+ url?: string;
69
+ session?: string;
70
+ token?: string;
71
+ debug?: boolean;
72
+ }): Config {
73
+ const configFile = loadConfigFile();
74
+
75
+ // Priority: CLI > Environment > Config file
76
+ let url = cliOptions.url ?? process.env.TENTICKLE_URL ?? configFile?.defaultUrl;
77
+
78
+ // Resolve alias if URL looks like one
79
+ if (url && !url.includes("://")) {
80
+ url = resolveAlias(url, configFile);
81
+ }
82
+
83
+ const sessionId = cliOptions.session ?? process.env.TENTICKLE_SESSION;
84
+
85
+ const token = cliOptions.token ?? process.env.TENTICKLE_TOKEN ?? configFile?.defaultToken;
86
+
87
+ const debug =
88
+ cliOptions.debug || process.env.TENTICKLE_DEBUG === "1" || configFile?.debug || false;
89
+
90
+ return {
91
+ url,
92
+ sessionId,
93
+ token,
94
+ debug,
95
+ };
96
+ }
97
+
98
+ /**
99
+ * Save configuration
100
+ */
101
+ export function saveConfig(config: Partial<ConfigFile>): void {
102
+ const configPath = getConfigPath();
103
+ const configDir = path.dirname(configPath);
104
+
105
+ // Ensure directory exists
106
+ if (!fs.existsSync(configDir)) {
107
+ fs.mkdirSync(configDir, { recursive: true });
108
+ }
109
+
110
+ // Load existing config
111
+ let existing: ConfigFile = {};
112
+ try {
113
+ if (fs.existsSync(configPath)) {
114
+ existing = JSON.parse(fs.readFileSync(configPath, "utf-8"));
115
+ }
116
+ } catch {
117
+ // Ignore
118
+ }
119
+
120
+ // Merge and save
121
+ const merged = { ...existing, ...config };
122
+ fs.writeFileSync(configPath, JSON.stringify(merged, null, 2));
123
+ }
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @agentick/cli - Terminal client for Agentick agents
3
+ *
4
+ * Provides interactive and non-interactive modes for communicating
5
+ * with Agentick servers.
6
+ *
7
+ * @module @agentick/cli
8
+ */
9
+
10
+ export { CLI, createCLI, type CLIConfig, type CLIEvents } from "./cli.js";
11
+ export { ChatSession, type ChatSessionOptions } from "./chat-session.js";
12
+ export { Renderer, type RendererOptions } from "./ui/renderer.js";
package/src/types.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Type declarations for modules without types
3
+ */
4
+
5
+ declare module "marked-terminal" {
6
+ import type { MarkedExtension } from "marked";
7
+
8
+ interface MarkedTerminalOptions {
9
+ code?: (code: string) => string;
10
+ blockquote?: (quote: string) => string;
11
+ html?: (html: string) => string;
12
+ heading?: (text: string) => string;
13
+ firstHeading?: (text: string) => string;
14
+ hr?: () => string;
15
+ listitem?: (text: string) => string;
16
+ table?: (header: string, body: string) => string;
17
+ paragraph?: (text: string) => string;
18
+ strong?: (text: string) => string;
19
+ em?: (text: string) => string;
20
+ codespan?: (code: string) => string;
21
+ del?: (text: string) => string;
22
+ link?: (href: string, title: string | null, text: string) => string;
23
+ href?: (href: string) => string;
24
+ reflowText?: boolean;
25
+ width?: number;
26
+ showSectionPrefix?: boolean;
27
+ unescape?: boolean;
28
+ emoji?: boolean;
29
+ tab?: number;
30
+ }
31
+
32
+ export function markedTerminal(options?: MarkedTerminalOptions): MarkedExtension;
33
+ }