@castari/cli 0.0.5 → 0.0.7
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/client-id.d.ts +1 -0
- package/dist/commands/client-id.js +25 -0
- package/dist/commands/deploy.js +8 -1
- package/dist/commands/generate-client-id.d.ts +1 -0
- package/dist/commands/generate-client-id.js +57 -0
- package/dist/commands/init.js +8 -6
- package/dist/commands/start.js +9 -3
- package/dist/index.js +5 -1
- package/dist/utils/client-auth.d.ts +11 -0
- package/dist/utils/client-auth.js +21 -0
- package/dist/utils/client-id.d.ts +10 -0
- package/dist/utils/client-id.js +36 -0
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveClientId(): Promise<string>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
const CONFIG_PATH = join(homedir(), '.castari', 'client.json');
|
|
7
|
+
export async function resolveClientId() {
|
|
8
|
+
const envId = process.env.CASTARI_CLIENT_ID;
|
|
9
|
+
if (envId)
|
|
10
|
+
return envId.trim();
|
|
11
|
+
if (existsSync(CONFIG_PATH)) {
|
|
12
|
+
try {
|
|
13
|
+
const raw = await readFile(CONFIG_PATH, 'utf-8');
|
|
14
|
+
const data = JSON.parse(raw);
|
|
15
|
+
if (data.clientId)
|
|
16
|
+
return data.clientId.trim();
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// ignore and fall through
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
console.error(chalk.red('❌ CASTARI_CLIENT_ID is required.'));
|
|
23
|
+
console.error(chalk.white('Run `castari generate-client-id` to create one (stored in ~/.castari/client.json), or set CASTARI_CLIENT_ID in your environment.'));
|
|
24
|
+
throw new Error('Missing CASTARI_CLIENT_ID');
|
|
25
|
+
}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { readFile } from 'fs/promises';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import AdmZip from 'adm-zip';
|
|
4
|
+
import { getClientAuthOrExit } from '../utils/client-auth';
|
|
4
5
|
export async function deploy(options) {
|
|
6
|
+
const { clientId, apiKey } = await getClientAuthOrExit();
|
|
5
7
|
// Read package.json to get default snapshot name
|
|
6
8
|
let snapshotName = options.snapshot;
|
|
7
9
|
if (!snapshotName) {
|
|
@@ -43,17 +45,22 @@ export async function deploy(options) {
|
|
|
43
45
|
const formData = new FormData();
|
|
44
46
|
formData.append('file', new Blob([zipBuffer]), 'source.zip');
|
|
45
47
|
formData.append('snapshot', snapshotName);
|
|
48
|
+
formData.append('clientId', clientId);
|
|
46
49
|
try {
|
|
50
|
+
const headers = {};
|
|
51
|
+
if (apiKey)
|
|
52
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
47
53
|
const response = await fetch(`${platformUrl}/deploy`, {
|
|
48
54
|
method: 'POST',
|
|
49
55
|
body: formData,
|
|
56
|
+
headers,
|
|
50
57
|
});
|
|
51
58
|
if (!response.ok) {
|
|
52
59
|
const errorText = await response.text();
|
|
53
60
|
throw new Error(`Platform error (${response.status}): ${errorText}`);
|
|
54
61
|
}
|
|
55
62
|
const result = await response.json();
|
|
56
|
-
console.log(chalk.green(`✅ Snapshot "${result.snapshot}" created successfully
|
|
63
|
+
console.log(chalk.green(`✅ Snapshot "${result.snapshot}" created successfully for client ${clientId}`));
|
|
57
64
|
}
|
|
58
65
|
catch (err) {
|
|
59
66
|
console.error(chalk.red('Deploy failed:'), err.message || err);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateClientId(): Promise<void>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { saveClientAuth } from '../utils/client-auth';
|
|
5
|
+
export async function generateClientId() {
|
|
6
|
+
const platformUrl = process.env.CASTARI_PLATFORM_URL || 'http://localhost:3000';
|
|
7
|
+
const mintOnline = async () => {
|
|
8
|
+
try {
|
|
9
|
+
const res = await fetch(`${platformUrl}/client-ids`, { method: 'POST' });
|
|
10
|
+
if (!res.ok) {
|
|
11
|
+
const text = await res.text();
|
|
12
|
+
throw new Error(text || `status ${res.status}`);
|
|
13
|
+
}
|
|
14
|
+
const data = (await res.json());
|
|
15
|
+
if (!data.clientId)
|
|
16
|
+
throw new Error('No clientId returned');
|
|
17
|
+
await saveClientAuth({
|
|
18
|
+
clientId: data.clientId,
|
|
19
|
+
apiKey: data.apiKey,
|
|
20
|
+
source: 'online',
|
|
21
|
+
platformUrl,
|
|
22
|
+
});
|
|
23
|
+
console.log(chalk.green(`✅ Client ID minted: ${data.clientId}`));
|
|
24
|
+
if (data.apiKey) {
|
|
25
|
+
console.log(chalk.green(`✅ API Key issued`));
|
|
26
|
+
}
|
|
27
|
+
console.log(chalk.white(`Saved to ~/.castari/client.json`));
|
|
28
|
+
return data.clientId;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error(chalk.yellow(`⚠️ Failed to mint via platform: ${err?.message || err}`));
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const onlineId = await mintOnline();
|
|
36
|
+
if (onlineId)
|
|
37
|
+
return;
|
|
38
|
+
const { confirmOffline } = await inquirer.prompt([
|
|
39
|
+
{
|
|
40
|
+
type: 'confirm',
|
|
41
|
+
name: 'confirmOffline',
|
|
42
|
+
message: 'Generate an offline client ID instead?',
|
|
43
|
+
default: false,
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
if (!confirmOffline) {
|
|
47
|
+
console.error(chalk.red('Aborted. No client ID generated.'));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
const offlineId = randomUUID();
|
|
51
|
+
await saveClientAuth({
|
|
52
|
+
clientId: offlineId,
|
|
53
|
+
source: 'offline',
|
|
54
|
+
});
|
|
55
|
+
console.log(chalk.green(`✅ Offline client ID generated: ${offlineId}`));
|
|
56
|
+
console.log(chalk.white(`Saved to ~/.castari/client.json`));
|
|
57
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function init(options = {}) {
|
|
|
25
25
|
start: 'castari start',
|
|
26
26
|
},
|
|
27
27
|
dependencies: {
|
|
28
|
-
'@castari/sdk': '^0.0.
|
|
28
|
+
'@castari/sdk': '^0.0.6',
|
|
29
29
|
'@anthropic-ai/claude-agent-sdk': '^0.1.44',
|
|
30
30
|
},
|
|
31
31
|
castari: {
|
|
@@ -65,7 +65,7 @@ serve({
|
|
|
65
65
|
systemPrompt: 'You are a helpful Castari agent.',
|
|
66
66
|
})
|
|
67
67
|
`;
|
|
68
|
-
const envExample = `ANTHROPIC_API_KEY=sk-ant-...\n# CASTARI_PLATFORM_URL=https://api.castari.com\n`;
|
|
68
|
+
const envExample = `ANTHROPIC_API_KEY=sk-ant-...\nCASTARI_CLIENT_ID=your-client-id\n# CASTARI_PLATFORM_URL=https://api.castari.com\n`;
|
|
69
69
|
await writeFile('package.json', JSON.stringify(packageJson, null, 2));
|
|
70
70
|
await writeFile('tsconfig.json', JSON.stringify(tsConfig, null, 2));
|
|
71
71
|
await writeFile('.env.example', envExample);
|
|
@@ -93,7 +93,7 @@ async function initDemo() {
|
|
|
93
93
|
start: 'bun run src/agent.ts',
|
|
94
94
|
},
|
|
95
95
|
dependencies: {
|
|
96
|
-
'@castari/sdk': '^0.0.
|
|
96
|
+
'@castari/sdk': '^0.0.6',
|
|
97
97
|
'@anthropic-ai/claude-agent-sdk': '^0.1.50',
|
|
98
98
|
},
|
|
99
99
|
castari: {
|
|
@@ -111,7 +111,7 @@ async function initDemo() {
|
|
|
111
111
|
},
|
|
112
112
|
include: ['src'],
|
|
113
113
|
});
|
|
114
|
-
await writeFile(join(agentDir, '.env.example'), 'ANTHROPIC_API_KEY=sk-ant-...\n# CASTARI_PLATFORM_URL=http://localhost:3000\n');
|
|
114
|
+
await writeFile(join(agentDir, '.env.example'), 'ANTHROPIC_API_KEY=sk-ant-...\nCASTARI_CLIENT_ID=your-client-id\n# CASTARI_PLATFORM_URL=http://localhost:3000\n');
|
|
115
115
|
await writeFile(join(agentDir, 'src', 'agent.ts'), `import { serve } from '@castari/sdk'
|
|
116
116
|
|
|
117
117
|
serve({
|
|
@@ -125,6 +125,7 @@ serve({
|
|
|
125
125
|
`);
|
|
126
126
|
// Web scaffold
|
|
127
127
|
await mkdir(join(webDir, 'app', 'api', 'chat'), { recursive: true });
|
|
128
|
+
await mkdir(join(webDir, 'lib'), { recursive: true });
|
|
128
129
|
await writeJson(join(webDir, 'package.json'), {
|
|
129
130
|
name: 'castari-demo-web',
|
|
130
131
|
version: '0.1.0',
|
|
@@ -135,7 +136,7 @@ serve({
|
|
|
135
136
|
start: 'next start',
|
|
136
137
|
},
|
|
137
138
|
dependencies: {
|
|
138
|
-
'@castari/sdk': '^0.0.
|
|
139
|
+
'@castari/sdk': '^0.0.6',
|
|
139
140
|
next: '14.2.3',
|
|
140
141
|
react: '18.3.1',
|
|
141
142
|
'react-dom': '18.3.1',
|
|
@@ -186,7 +187,7 @@ const nextConfig = {
|
|
|
186
187
|
|
|
187
188
|
export default nextConfig
|
|
188
189
|
`);
|
|
189
|
-
await writeFile(join(webDir, '.env.example'), 'ANTHROPIC_API_KEY=sk-ant-...\n# CASTARI_PLATFORM_URL=http://localhost:3000\n# CASTARI_DEBUG=false\n');
|
|
190
|
+
await writeFile(join(webDir, '.env.example'), 'ANTHROPIC_API_KEY=sk-ant-...\nCASTARI_CLIENT_ID=your-client-id\n# CASTARI_PLATFORM_URL=http://localhost:3000\n# CASTARI_DEBUG=false\n');
|
|
190
191
|
await writeFile(join(webDir, 'app', 'globals.css'), `:root {
|
|
191
192
|
color-scheme: light;
|
|
192
193
|
background: #f5f5f5;
|
|
@@ -497,6 +498,7 @@ async function createClient() {
|
|
|
497
498
|
volume: VOLUME,
|
|
498
499
|
labels: LABELS,
|
|
499
500
|
platformUrl: process.env.CASTARI_PLATFORM_URL,
|
|
501
|
+
clientId: process.env.CASTARI_CLIENT_ID,
|
|
500
502
|
anthropicApiKey,
|
|
501
503
|
debug: process.env.CASTARI_DEBUG === 'true',
|
|
502
504
|
})
|
package/dist/commands/start.js
CHANGED
|
@@ -2,6 +2,7 @@ import { CastariClient } from '@castari/sdk/client';
|
|
|
2
2
|
import { readFile } from 'fs/promises';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import dotenv from 'dotenv';
|
|
5
|
+
import { getClientAuthOrExit } from '../utils/client-auth';
|
|
5
6
|
export async function start(options) {
|
|
6
7
|
// Load .env from current directory
|
|
7
8
|
dotenv.config();
|
|
@@ -18,6 +19,7 @@ export async function start(options) {
|
|
|
18
19
|
catch (e) {
|
|
19
20
|
// Ignore if package.json missing
|
|
20
21
|
}
|
|
22
|
+
const { clientId, apiKey } = await getClientAuthOrExit();
|
|
21
23
|
if (!snapshotName) {
|
|
22
24
|
console.error(chalk.red('Error: Snapshot name is required.'));
|
|
23
25
|
process.exit(1);
|
|
@@ -27,13 +29,17 @@ export async function start(options) {
|
|
|
27
29
|
console.log(chalk.blue(`📦 Using volume: ${volumeName}`));
|
|
28
30
|
}
|
|
29
31
|
const platformUrl = process.env.CASTARI_PLATFORM_URL;
|
|
30
|
-
const
|
|
32
|
+
const clientOptions = {
|
|
31
33
|
snapshot: snapshotName,
|
|
32
34
|
volume: volumeName,
|
|
33
35
|
platformUrl,
|
|
34
36
|
debug: true, // Enable debug logs for CLI
|
|
35
|
-
anthropicApiKey: process.env.ANTHROPIC_API_KEY
|
|
36
|
-
|
|
37
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
38
|
+
clientId,
|
|
39
|
+
};
|
|
40
|
+
if (apiKey)
|
|
41
|
+
clientOptions.platformApiKey = apiKey;
|
|
42
|
+
const client = new CastariClient(clientOptions);
|
|
37
43
|
try {
|
|
38
44
|
await client.start();
|
|
39
45
|
console.log(chalk.green('✅ Agent started!'));
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { init } from './commands/init';
|
|
|
4
4
|
import { deploy } from './commands/deploy';
|
|
5
5
|
import { start } from './commands/start';
|
|
6
6
|
import { dev } from './commands/dev';
|
|
7
|
+
import { generateClientId } from './commands/generate-client-id';
|
|
7
8
|
const cli = cac('castari');
|
|
8
9
|
cli
|
|
9
10
|
.command('init', 'Initialize a new Castari agent project')
|
|
@@ -22,6 +23,9 @@ cli
|
|
|
22
23
|
cli
|
|
23
24
|
.command('dev', 'Run the agent locally for development')
|
|
24
25
|
.action(dev);
|
|
26
|
+
cli
|
|
27
|
+
.command('generate-client-id', 'Generate a new Castari Client ID and save it locally')
|
|
28
|
+
.action(generateClientId);
|
|
25
29
|
cli.help();
|
|
26
|
-
cli.version('0.0.
|
|
30
|
+
cli.version('0.0.7');
|
|
27
31
|
cli.parse();
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ClientAuth = {
|
|
2
|
+
clientId: string;
|
|
3
|
+
apiKey?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function getClientAuthOrExit(): Promise<ClientAuth>;
|
|
6
|
+
export declare function saveClientAuth(data: {
|
|
7
|
+
clientId: string;
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
source?: 'online' | 'offline';
|
|
10
|
+
platformUrl?: string;
|
|
11
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getClientIdOrExit, loadClientId, saveClientId } from './client-id';
|
|
2
|
+
export async function getClientAuthOrExit() {
|
|
3
|
+
const clientId = await getClientIdOrExit();
|
|
4
|
+
const stored = await loadClientId();
|
|
5
|
+
const apiKey = process.env.CASTARI_API_KEY ||
|
|
6
|
+
stored?.apiKey;
|
|
7
|
+
if (!apiKey) {
|
|
8
|
+
// We allow missing apiKey for now (platform may allow unauthenticated in dev)
|
|
9
|
+
return { clientId };
|
|
10
|
+
}
|
|
11
|
+
return { clientId, apiKey };
|
|
12
|
+
}
|
|
13
|
+
export async function saveClientAuth(data) {
|
|
14
|
+
await saveClientId({
|
|
15
|
+
clientId: data.clientId,
|
|
16
|
+
apiKey: data.apiKey,
|
|
17
|
+
source: data.source,
|
|
18
|
+
createdAt: new Date().toISOString(),
|
|
19
|
+
platformUrl: data.platformUrl,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type StoredClientId = {
|
|
2
|
+
clientId: string;
|
|
3
|
+
source?: 'online' | 'offline';
|
|
4
|
+
createdAt?: string;
|
|
5
|
+
platformUrl?: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function loadClientId(): Promise<StoredClientId | null>;
|
|
9
|
+
export declare function saveClientId(data: StoredClientId): Promise<void>;
|
|
10
|
+
export declare function getClientIdOrExit(explicit?: string): Promise<string>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
const CONFIG_DIR = join(homedir(), '.castari');
|
|
6
|
+
const CONFIG_PATH = join(CONFIG_DIR, 'client.json');
|
|
7
|
+
export async function loadClientId() {
|
|
8
|
+
try {
|
|
9
|
+
const raw = await readFile(CONFIG_PATH, 'utf-8');
|
|
10
|
+
const parsed = JSON.parse(raw);
|
|
11
|
+
if (parsed.clientId && typeof parsed.clientId === 'string') {
|
|
12
|
+
return parsed;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function saveClientId(data) {
|
|
21
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
22
|
+
await writeFile(CONFIG_PATH, JSON.stringify(data, null, 2));
|
|
23
|
+
}
|
|
24
|
+
export async function getClientIdOrExit(explicit) {
|
|
25
|
+
const envId = process.env.CASTARI_CLIENT_ID;
|
|
26
|
+
if (explicit)
|
|
27
|
+
return explicit;
|
|
28
|
+
if (envId)
|
|
29
|
+
return envId;
|
|
30
|
+
const stored = await loadClientId();
|
|
31
|
+
if (stored?.clientId)
|
|
32
|
+
return stored.clientId;
|
|
33
|
+
console.error(chalk.red('CASTARI_CLIENT_ID is required.'));
|
|
34
|
+
console.error(chalk.white('Run `castari generate-client-id` to create one, or set CASTARI_CLIENT_ID in your environment.'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@castari/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"castari": "./dist/index.js"
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@anthropic-ai/claude-agent-sdk": "^0.1.44",
|
|
21
|
-
"@castari/sdk": "^0.0.
|
|
21
|
+
"@castari/sdk": "^0.0.6",
|
|
22
22
|
"adm-zip": "^0.5.16",
|
|
23
23
|
"cac": "^6.7.14",
|
|
24
24
|
"chalk": "^5.3.0",
|