@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.
- package/dist/commands/deploy.d.ts +3 -0
- package/dist/commands/deploy.js +64 -0
- package/dist/commands/dev.d.ts +1 -0
- package/dist/commands/dev.js +17 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +73 -0
- package/dist/commands/start.d.ts +5 -0
- package/dist/commands/start.js +86 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +26 -0
- package/package.json +33 -0
|
@@ -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,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
|
+
}
|
package/dist/index.d.ts
ADDED
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
|
+
}
|