@castari/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.
@@ -0,0 +1,3 @@
1
+ export declare function deploy(options: {
2
+ snapshot?: string;
3
+ }): Promise<void>;
@@ -0,0 +1,64 @@
1
+ import { Daytona, Image } from '@daytonaio/sdk';
2
+ import { readFile } from 'fs/promises';
3
+ import chalk from 'chalk';
4
+ export async function deploy(options) {
5
+ const apiKey = process.env.DAYTONA_API_KEY;
6
+ if (!apiKey) {
7
+ console.error(chalk.red('Error: DAYTONA_API_KEY is required.'));
8
+ process.exit(1);
9
+ }
10
+ // Read package.json to get default snapshot name
11
+ let snapshotName = options.snapshot;
12
+ if (!snapshotName) {
13
+ try {
14
+ const pkg = JSON.parse(await readFile('package.json', 'utf-8'));
15
+ snapshotName = pkg.name;
16
+ }
17
+ catch (e) {
18
+ console.warn(chalk.yellow('Warning: Could not read package.json. Using default name.'));
19
+ snapshotName = 'castari-agent';
20
+ }
21
+ }
22
+ if (!snapshotName) {
23
+ console.error(chalk.red('Error: Snapshot name is required (via --snapshot or package.json name).'));
24
+ process.exit(1);
25
+ }
26
+ console.log(chalk.blue(`🧱 Building Daytona snapshot "${snapshotName}"...`));
27
+ const daytona = new Daytona({
28
+ apiKey,
29
+ apiUrl: process.env.DAYTONA_API_URL,
30
+ target: process.env.DAYTONA_TARGET,
31
+ });
32
+ const projectRoot = process.cwd();
33
+ const workspaceMount = '/home/daytona/agent-workspace';
34
+ // Use official Bun image
35
+ const image = Image.base('oven/bun:1.3.1')
36
+ .addLocalDir(projectRoot, '/home/daytona/app')
37
+ .workdir('/home/daytona/app')
38
+ .runCommands('apt-get update && apt-get install -y git', 'bun install', `mkdir -p ${workspaceMount}`)
39
+ .entrypoint(['sleep', 'infinity']);
40
+ try {
41
+ // Try to delete existing snapshot to allow overwrite
42
+ try {
43
+ await daytona.snapshot.delete(snapshotName);
44
+ console.log(chalk.yellow(`🗑️ Deleted existing snapshot "${snapshotName}"`));
45
+ }
46
+ catch (e) {
47
+ // Ignore delete error (e.g. not found)
48
+ }
49
+ await daytona.snapshot.create({
50
+ name: snapshotName,
51
+ image,
52
+ }, {
53
+ onLogs: log => {
54
+ if (log)
55
+ process.stdout.write(log);
56
+ },
57
+ });
58
+ console.log(chalk.green(`✅ Snapshot "${snapshotName}" created successfully!`));
59
+ }
60
+ catch (err) {
61
+ console.error(chalk.red('Snapshot build failed:'), err.message || err);
62
+ process.exit(1);
63
+ }
64
+ }
@@ -0,0 +1 @@
1
+ export declare function dev(): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import { pathToFileURL } from 'url';
2
+ import { join } from 'path';
3
+ import chalk from 'chalk';
4
+ export async function dev() {
5
+ console.log(chalk.blue('🛠️ Starting local development server...'));
6
+ try {
7
+ const agentPath = join(process.cwd(), 'src', 'agent.ts');
8
+ // Dynamic import of the agent file.
9
+ // Since the agent file calls `serve()`, this will start the server.
10
+ await import(pathToFileURL(agentPath).href);
11
+ }
12
+ catch (err) {
13
+ console.error(chalk.red('Failed to start local agent:'), err.message || err);
14
+ console.log(chalk.white('Make sure src/agent.ts exists and exports/calls serve().'));
15
+ process.exit(1);
16
+ }
17
+ }
@@ -0,0 +1 @@
1
+ export declare function init(): Promise<void>;
@@ -0,0 +1,73 @@
1
+ import inquirer from 'inquirer';
2
+ import { writeFile, mkdir } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import chalk from 'chalk';
5
+ export async function init() {
6
+ console.log(chalk.blue('🤖 Initializing new Castari agent project...'));
7
+ const answers = await inquirer.prompt([
8
+ {
9
+ type: 'input',
10
+ name: 'name',
11
+ message: 'Agent name (used for snapshot):',
12
+ default: 'my-castari-agent',
13
+ },
14
+ ]);
15
+ const packageJson = {
16
+ name: answers.name,
17
+ version: '0.1.0',
18
+ type: 'module',
19
+ scripts: {
20
+ dev: 'castari dev',
21
+ deploy: 'castari deploy',
22
+ start: 'castari start',
23
+ },
24
+ dependencies: {
25
+ '@castari/sdk': 'workspace:*',
26
+ '@anthropic-ai/claude-agent-sdk': '^0.1.44',
27
+ },
28
+ castari: {
29
+ volume: 'default-data',
30
+ },
31
+ };
32
+ const tsConfig = {
33
+ compilerOptions: {
34
+ target: 'ESNext',
35
+ module: 'ESNext',
36
+ moduleResolution: 'bundler',
37
+ strict: true,
38
+ skipLibCheck: true,
39
+ },
40
+ };
41
+ const agentTs = `import { serve } from '@castari/sdk/server'
42
+ import { tool } from '@castari/sdk'
43
+
44
+ // Define your agent tools and logic here
45
+ const myTool = tool({
46
+ name: 'hello',
47
+ description: 'Say hello',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: {
51
+ name: { type: 'string' },
52
+ },
53
+ },
54
+ handler: async ({ name }) => {
55
+ return \`Hello, \${name}!\`
56
+ },
57
+ })
58
+
59
+ // Start the agent server
60
+ serve({
61
+ tools: [myTool],
62
+ systemPrompt: 'You are a helpful Castari agent.',
63
+ })
64
+ `;
65
+ const envExample = `ANTHROPIC_API_KEY=sk-ant-...\nDAYTONA_API_KEY=...\n`;
66
+ await writeFile('package.json', JSON.stringify(packageJson, null, 2));
67
+ await writeFile('tsconfig.json', JSON.stringify(tsConfig, null, 2));
68
+ await writeFile('.env.example', envExample);
69
+ await mkdir('src', { recursive: true });
70
+ await writeFile(join('src', 'agent.ts'), agentTs);
71
+ console.log(chalk.green('✅ Project initialized!'));
72
+ console.log(chalk.white('Run `bun install` to install dependencies.'));
73
+ }
@@ -0,0 +1,5 @@
1
+ export declare function start(options: {
2
+ snapshot?: string;
3
+ volume?: string;
4
+ id?: string;
5
+ }): Promise<void>;
@@ -0,0 +1,86 @@
1
+ import { CastariClient } from '@castari/sdk/client';
2
+ import { readFile } from 'fs/promises';
3
+ import chalk from 'chalk';
4
+ import dotenv from 'dotenv';
5
+ export async function start(options) {
6
+ // Load .env from current directory
7
+ dotenv.config();
8
+ const apiKey = process.env.DAYTONA_API_KEY;
9
+ if (!apiKey) {
10
+ console.error(chalk.red('Error: DAYTONA_API_KEY is required.'));
11
+ process.exit(1);
12
+ }
13
+ // Read package.json for defaults
14
+ let snapshotName = options.snapshot;
15
+ let volumeName = options.volume;
16
+ try {
17
+ const pkg = JSON.parse(await readFile('package.json', 'utf-8'));
18
+ if (!snapshotName)
19
+ snapshotName = pkg.name;
20
+ if (!volumeName)
21
+ volumeName = pkg.castari?.volume;
22
+ }
23
+ catch (e) {
24
+ // Ignore if package.json missing
25
+ }
26
+ if (!snapshotName) {
27
+ console.error(chalk.red('Error: Snapshot name is required.'));
28
+ process.exit(1);
29
+ }
30
+ console.log(chalk.blue(`🚀 Starting agent "${snapshotName}"...`));
31
+ if (volumeName) {
32
+ console.log(chalk.blue(`📦 Using volume: ${volumeName}`));
33
+ }
34
+ const client = new CastariClient({
35
+ snapshot: snapshotName,
36
+ volume: volumeName,
37
+ daytonaApiKey: apiKey,
38
+ debug: true, // Enable debug logs for CLI
39
+ });
40
+ try {
41
+ await client.start();
42
+ console.log(chalk.green('✅ Agent started!'));
43
+ console.log(chalk.cyan('Type a message to chat with the agent (Ctrl+C to exit):'));
44
+ const stdin = process.stdin;
45
+ stdin.addListener('data', (d) => {
46
+ const input = d.toString().trim();
47
+ if (input) {
48
+ client.send({
49
+ type: 'user_message',
50
+ data: {
51
+ type: 'user',
52
+ message: {
53
+ role: 'user',
54
+ content: input
55
+ }
56
+ }
57
+ });
58
+ }
59
+ });
60
+ client.onMessage((msg) => {
61
+ if (msg.type === 'sdk_message') {
62
+ const data = msg.data;
63
+ if (data.type === 'assistant_message') {
64
+ // Handle text content
65
+ if (typeof data.message.content === 'string') {
66
+ console.log(chalk.green('Agent:'), data.message.content);
67
+ }
68
+ else if (Array.isArray(data.message.content)) {
69
+ data.message.content.forEach((block) => {
70
+ if (block.type === 'text') {
71
+ console.log(chalk.green('Agent:'), block.text);
72
+ }
73
+ });
74
+ }
75
+ }
76
+ }
77
+ else if (msg.type === 'error') {
78
+ console.error(chalk.red('Error:'), msg.error);
79
+ }
80
+ });
81
+ }
82
+ catch (err) {
83
+ console.error(chalk.red('Failed to start agent:'), err.message || err);
84
+ process.exit(1);
85
+ }
86
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bun
2
+ import { cac } from 'cac';
3
+ import { init } from './commands/init';
4
+ import { deploy } from './commands/deploy';
5
+ import { start } from './commands/start';
6
+ import { dev } from './commands/dev';
7
+ const cli = cac('castari');
8
+ cli
9
+ .command('init', 'Initialize a new Castari agent project')
10
+ .action(init);
11
+ cli
12
+ .command('deploy', 'Build and deploy the agent snapshot to Daytona')
13
+ .option('--snapshot <name>', 'Snapshot name (overrides package.json)')
14
+ .action(deploy);
15
+ cli
16
+ .command('start', 'Start a Daytona sandbox for the agent')
17
+ .option('--snapshot <name>', 'Snapshot name')
18
+ .option('--volume <name>', 'Volume name for persistence')
19
+ .option('--id <id>', 'Custom sandbox ID')
20
+ .action(start);
21
+ cli
22
+ .command('dev', 'Run the agent locally for development')
23
+ .action(dev);
24
+ cli.help();
25
+ cli.version('0.0.1');
26
+ cli.parse();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@castari/cli",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "bin": {
6
+ "castari": "./dist/index.js"
7
+ },
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "license": "UNLICENSED",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "bun run src/index.ts"
18
+ },
19
+ "dependencies": {
20
+ "cac": "^6.7.14",
21
+ "inquirer": "^9.2.12",
22
+ "chalk": "^5.3.0",
23
+ "dotenv": "^16.3.1",
24
+ "@daytonaio/sdk": "^0.115.2",
25
+ "@castari/sdk": "^0.0.1",
26
+ "@anthropic-ai/claude-agent-sdk": "^0.1.44"
27
+ },
28
+ "devDependencies": {
29
+ "@types/inquirer": "^9.0.7",
30
+ "@types/node": "^20.10.0",
31
+ "typescript": "^5.6.3"
32
+ }
33
+ }