@castari/sdk 0.1.5 → 0.2.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.
package/dist/client.d.ts CHANGED
@@ -1,55 +1,56 @@
1
- import type { QueryConfig, WSInputMessage, WSOutputMessage } from './types';
2
- export * from './types';
1
+ import { AgentsAPI } from './agents.js';
2
+ import { UsageAPI } from './usage.js';
3
+ import { AuthAPI } from './auth.js';
4
+ import type { CastariClientOptions } from './types.js';
3
5
  /**
4
- * Configuration options for the Castari Client.
6
+ * Main client for interacting with the Castari API
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { CastariClient } from '@castari/sdk';
11
+ *
12
+ * // Uses credentials from ~/.castari or CASTARI_API_KEY env var
13
+ * const client = new CastariClient();
14
+ *
15
+ * // Or provide credentials explicitly
16
+ * const client = new CastariClient({ apiKey: 'cap_xxxxx' });
17
+ *
18
+ * // List agents
19
+ * const agents = await client.agents.list();
20
+ *
21
+ * // Deploy an agent
22
+ * await client.agents.deploy('my-agent');
23
+ *
24
+ * // Invoke an agent
25
+ * const result = await client.agents.invoke('my-agent', { prompt: 'Hello!' });
26
+ * ```
5
27
  */
6
- export interface ClientOptions extends Partial<QueryConfig> {
7
- /** Local/custom connection URL (e.g., 'http://localhost:3000'). If omitted, Platform mode is used. */
8
- connectionUrl?: string;
9
- /** Anthropic API key (required unless present in process.env.ANTHROPIC_API_KEY) */
10
- anthropicApiKey?: string;
11
- /** Castari client ID (required for platform mode; otherwise read from env) */
12
- clientId?: string;
13
- /** Castari platform API key (used for auth when contacting the platform) */
14
- platformApiKey?: string;
15
- /** Enable debug logging */
16
- debug?: boolean;
17
- /** Snapshot name to deploy/start */
18
- snapshot?: string;
19
- /** Optional labels to apply to the sandbox (and filter by for reuse) */
20
- labels?: Record<string, string>;
21
- /** Optional volume name to mount at /home/castari/agent-workspace */
22
- volume?: string;
23
- /** Castari Platform API URL. Defaults to https://api.castari.com (or localhost in dev) */
24
- platformUrl?: string;
25
- /** Optional sessionId to resume */
26
- resume?: string;
27
- /**
28
- * Use the platform API as a WebSocket proxy instead of connecting directly to the sandbox.
29
- * Defaults to true for reliability. Set to false to connect directly to the sandbox.
30
- */
31
- useProxy?: boolean;
32
- }
33
28
  export declare class CastariClient {
34
- private ws?;
35
29
  private options;
36
- private messageHandlers;
37
- private closeHandlers;
38
- private sandboxId?;
39
- private resolvedClientId?;
40
- private resolvedPlatformApiKey?;
41
- constructor(options?: ClientOptions);
42
- /** Check if the WebSocket is currently connected */
43
- isConnected(): boolean;
44
- start(): Promise<void>;
45
- private setupLocalConnection;
46
- private setupPlatformConnection;
47
- private handleMessage;
48
- onMessage(handler: (message: WSOutputMessage) => void): () => void;
49
- /** Register a callback for when the WebSocket connection closes */
50
- onClose(handler: (code: number, reason: string) => void): () => void;
51
- send(message: WSInputMessage): void;
52
- stop(options?: {
53
- delete?: boolean;
54
- }): Promise<void>;
30
+ private httpClient;
31
+ private initialized;
32
+ private initPromise;
33
+ /** API for managing agents */
34
+ readonly agents: AgentsAPI;
35
+ /** API for accessing usage statistics */
36
+ readonly usage: UsageAPI;
37
+ /** API for authentication operations */
38
+ readonly auth: AuthAPI;
39
+ /**
40
+ * Create a new Castari client
41
+ * @param options - Client configuration options
42
+ */
43
+ constructor(options?: CastariClientOptions);
44
+ /**
45
+ * Initialize the client by loading credentials from config
46
+ * This is called automatically before the first API request
47
+ */
48
+ private initialize;
49
+ private doInitialize;
50
+ /**
51
+ * Ensure the client is initialized before making requests
52
+ * @throws AuthenticationError if no credentials are available
53
+ */
54
+ ensureAuthenticated(): Promise<void>;
55
55
  }
56
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;IAkBZ,OAAO,CAAC,OAAO;IAjB3B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAA8B;IAEjD,8BAA8B;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAE3B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IAEzB,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAEvB;;;OAGG;gBACiB,OAAO,GAAE,oBAAyB;IAqBtD;;;OAGG;YACW,UAAU;YAaV,YAAY;IAY1B;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ3C"}
package/dist/client.js CHANGED
@@ -1,255 +1,102 @@
1
- export * from './types';
2
- const DEFAULT_LOCAL_URL = 'http://localhost:3000';
3
- const DEFAULT_PLATFORM_URL = 'https://castari-api-12511-04c55b73-g4p2s9om.onporter.run';
1
+ import { HttpClient } from './http.js';
2
+ import { AgentsAPI } from './agents.js';
3
+ import { UsageAPI } from './usage.js';
4
+ import { AuthAPI } from './auth.js';
5
+ import { getAuth } from './config.js';
6
+ import { AuthenticationError } from './errors.js';
7
+ /**
8
+ * Main client for interacting with the Castari API
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { CastariClient } from '@castari/sdk';
13
+ *
14
+ * // Uses credentials from ~/.castari or CASTARI_API_KEY env var
15
+ * const client = new CastariClient();
16
+ *
17
+ * // Or provide credentials explicitly
18
+ * const client = new CastariClient({ apiKey: 'cap_xxxxx' });
19
+ *
20
+ * // List agents
21
+ * const agents = await client.agents.list();
22
+ *
23
+ * // Deploy an agent
24
+ * await client.agents.deploy('my-agent');
25
+ *
26
+ * // Invoke an agent
27
+ * const result = await client.agents.invoke('my-agent', { prompt: 'Hello!' });
28
+ * ```
29
+ */
4
30
  export class CastariClient {
5
- ws;
6
31
  options;
7
- messageHandlers = [];
8
- closeHandlers = [];
9
- sandboxId;
10
- resolvedClientId;
11
- resolvedPlatformApiKey;
32
+ httpClient;
33
+ initialized = false;
34
+ initPromise = null;
35
+ /** API for managing agents */
36
+ agents;
37
+ /** API for accessing usage statistics */
38
+ usage;
39
+ /** API for authentication operations */
40
+ auth;
41
+ /**
42
+ * Create a new Castari client
43
+ * @param options - Client configuration options
44
+ */
12
45
  constructor(options = {}) {
13
- this.options = {
14
- ...options,
15
- };
16
- }
17
- /** Check if the WebSocket is currently connected */
18
- isConnected() {
19
- return this.ws?.readyState === WebSocket.OPEN;
20
- }
21
- async start() {
22
- const anthropicApiKey = this.options.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
23
- if (!anthropicApiKey) {
24
- throw new Error('ANTHROPIC_API_KEY is required');
25
- }
26
- this.resolvedClientId =
27
- this.options.clientId || process.env.CASTARI_CLIENT_ID || undefined;
28
- this.resolvedPlatformApiKey =
29
- this.options.platformApiKey || process.env.CASTARI_API_KEY || undefined;
30
- const connection = this.options.connectionUrl
31
- ? await this.setupLocalConnection()
32
- : await this.setupPlatformConnection();
33
- if (this.options.debug) {
34
- console.log(`📡 Configuring server at ${connection.configUrl}...`);
35
- }
36
- const configPayload = {
37
- anthropicApiKey,
38
- agents: this.options.agents,
39
- allowedTools: this.options.allowedTools,
40
- systemPrompt: this.options.systemPrompt,
41
- model: this.options.model,
42
- resume: this.options.resume,
43
- };
44
- if (this.options.debug) {
45
- console.log(`📋 Config payload:`, JSON.stringify(configPayload, null, 2));
46
- }
47
- const configHeaders = {
48
- 'Content-Type': 'application/json',
49
- ...connection.authHeaders,
50
- };
51
- let configResponse = null;
52
- const maxConfigAttempts = 5;
53
- for (let attempt = 1; attempt <= maxConfigAttempts; attempt++) {
54
- configResponse = await fetch(connection.configUrl, {
55
- method: 'POST',
56
- headers: configHeaders,
57
- body: JSON.stringify(configPayload),
58
- }).catch(err => {
59
- if (this.options.debug) {
60
- console.warn(`⚠️ Config request failed on attempt ${attempt}:`, err);
61
- }
62
- return null;
63
- });
64
- if (configResponse && configResponse.ok)
65
- break;
66
- if (this.options.debug) {
67
- console.warn(`⚠️ Config attempt ${attempt} failed (status ${configResponse?.status ?? 'n/a'}).`);
68
- }
69
- if (attempt < maxConfigAttempts) {
70
- await new Promise(resolve => setTimeout(resolve, 3000));
71
- }
72
- }
73
- if (!configResponse || !configResponse.ok) {
74
- const errorText = configResponse ? await configResponse.text() : 'no response';
75
- if (connection.cleanup)
76
- await connection.cleanup();
77
- throw new Error(`Failed to configure server (status ${configResponse?.status ?? 'n/a'}): ${errorText}`);
78
- }
79
- const { connectionToken } = (await configResponse.json());
80
- if (!connectionToken) {
81
- if (connection.cleanup)
82
- await connection.cleanup();
83
- throw new Error('Server did not return a connectionToken');
84
- }
85
- const wsUrlParams = new URLSearchParams();
86
- wsUrlParams.set('token', connectionToken);
87
- // Add any auth params from platform (for sandbox proxy auth)
88
- if (connection.authParams) {
89
- for (const [key, value] of Object.entries(connection.authParams)) {
90
- wsUrlParams.set(key, value);
91
- }
92
- }
93
- const wsUrlJoiner = connection.wsUrl.includes('?') ? '&' : '?';
94
- const wsUrl = `${connection.wsUrl}${wsUrlJoiner}${wsUrlParams.toString()}`;
95
- if (this.options.debug) {
96
- console.log(`🔌 Connecting to WebSocket at ${wsUrl}...`);
97
- }
98
- return new Promise((resolve, reject) => {
99
- this.ws = new WebSocket(wsUrl);
100
- this.ws.onopen = () => {
101
- if (this.options.debug)
102
- console.log('✅ Connected to Castari Server');
103
- resolve();
104
- };
105
- this.ws.onmessage = event => {
106
- try {
107
- const message = JSON.parse(event.data.toString());
108
- this.handleMessage(message);
109
- }
110
- catch (error) {
111
- console.error('Failed to parse message:', error);
112
- }
113
- };
114
- this.ws.onerror = error => {
115
- console.error('WebSocket error:', error);
116
- reject(error);
117
- };
118
- this.ws.onclose = (event) => {
119
- if (this.options.debug)
120
- console.log(`👋 Disconnected (code=${event.code})`);
121
- this.closeHandlers.forEach(handler => handler(event.code, event.reason));
122
- };
46
+ this.options = options;
47
+ // Create HTTP client with provided or default base URL
48
+ this.httpClient = new HttpClient({
49
+ baseUrl: options.baseUrl || 'https://web-13239-04c55b73-wp4aqyqk.onporter.run',
123
50
  });
51
+ // Set auth if provided directly
52
+ if (options.apiKey) {
53
+ this.httpClient.setAuth('api_key', options.apiKey);
54
+ this.initialized = true;
55
+ }
56
+ else if (options.token) {
57
+ this.httpClient.setAuth('token', options.token);
58
+ this.initialized = true;
59
+ }
60
+ // Initialize API classes
61
+ this.agents = new AgentsAPI(this.httpClient);
62
+ this.usage = new UsageAPI(this.httpClient);
63
+ this.auth = new AuthAPI(this.httpClient);
124
64
  }
125
- async setupLocalConnection() {
126
- const baseUrl = (this.options.connectionUrl || DEFAULT_LOCAL_URL).replace(/\/$/, '');
127
- return {
128
- configUrl: `${baseUrl.replace('ws://', 'http://').replace('wss://', 'https://')}/config`,
129
- wsUrl: `${baseUrl.replace('http://', 'ws://').replace('https://', 'wss://')}/ws`,
130
- };
65
+ /**
66
+ * Initialize the client by loading credentials from config
67
+ * This is called automatically before the first API request
68
+ */
69
+ async initialize() {
70
+ if (this.initialized)
71
+ return;
72
+ // If already initializing, wait for it
73
+ if (this.initPromise) {
74
+ await this.initPromise;
75
+ return;
76
+ }
77
+ this.initPromise = this.doInitialize();
78
+ await this.initPromise;
131
79
  }
132
- async setupPlatformConnection() {
133
- if (!this.resolvedClientId) {
134
- throw new Error('CASTARI_CLIENT_ID is required when connecting via the Castari Platform');
135
- }
136
- const platformUrl = (this.options.platformUrl || process.env.CASTARI_PLATFORM_URL || DEFAULT_PLATFORM_URL).replace(/\/$/, '');
137
- if (this.options.debug) {
138
- console.log(`🚀 Requesting sandbox from ${platformUrl}...`);
139
- }
140
- const response = await fetch(`${platformUrl}/sandbox/start`, {
141
- method: 'POST',
142
- headers: {
143
- 'Content-Type': 'application/json',
144
- ...(this.resolvedPlatformApiKey
145
- ? { Authorization: `Bearer ${this.resolvedPlatformApiKey}` }
146
- : {}),
147
- },
148
- body: JSON.stringify({
149
- snapshot: this.options.snapshot,
150
- labels: this.options.labels,
151
- volume: this.options.volume,
152
- clientId: this.resolvedClientId
153
- })
154
- });
155
- if (!response.ok) {
156
- const errorText = await response.text();
157
- throw new Error(`Failed to start sandbox: ${errorText}`);
158
- }
159
- const { id, url, proxyUrl, authHeaders, authParams } = await response.json();
160
- this.sandboxId = id;
161
- // Default to proxy mode (true) unless explicitly disabled
162
- const useProxy = this.options.useProxy ?? (process.env.CASTARI_USE_PROXY !== 'false');
163
- if (this.options.debug) {
164
- console.log(`✅ Sandbox started: ${id} at ${url}`);
165
- if (useProxy && proxyUrl) {
166
- console.log(`🔀 Using proxy mode via ${proxyUrl}`);
80
+ async doInitialize() {
81
+ // Load auth from config if not provided
82
+ if (!this.options.apiKey && !this.options.token) {
83
+ const auth = await getAuth();
84
+ if (auth) {
85
+ this.httpClient.setAuth(auth.type, auth.value);
167
86
  }
168
87
  }
169
- // If proxy mode is enabled and we have a proxy URL, use it
170
- if (useProxy && proxyUrl) {
171
- // Proxy mode: connect through platform API
172
- const proxyConfigUrl = `${platformUrl}/proxy/${id}/config`;
173
- const proxyWsUrl = proxyUrl;
174
- return {
175
- configUrl: proxyConfigUrl,
176
- wsUrl: proxyWsUrl,
177
- // No auth headers/params needed - proxy handles sandbox auth
178
- cleanup: async () => {
179
- await this.stop({ delete: true });
180
- }
181
- };
182
- }
183
- // Direct mode: connect to sandbox directly
184
- const baseUrl = url.replace(/\/$/, '');
185
- const configUrl = `${baseUrl.replace('ws://', 'http://').replace('wss://', 'https://')}/config`;
186
- const wsUrlBase = `${baseUrl.replace('https://', 'wss://').replace('http://', 'ws://')}/ws`;
187
- return {
188
- configUrl,
189
- wsUrl: wsUrlBase,
190
- authHeaders,
191
- authParams,
192
- cleanup: async () => {
193
- await this.stop({ delete: true });
194
- }
195
- };
196
- }
197
- handleMessage(message) {
198
- if (this.options.debug) {
199
- console.log('📨 Received message:', JSON.stringify(message, null, 2));
200
- }
201
- this.messageHandlers.forEach(handler => handler(message));
88
+ this.initialized = true;
202
89
  }
203
- onMessage(handler) {
204
- this.messageHandlers.push(handler);
205
- return () => {
206
- this.messageHandlers = this.messageHandlers.filter(h => h !== handler);
207
- };
208
- }
209
- /** Register a callback for when the WebSocket connection closes */
210
- onClose(handler) {
211
- this.closeHandlers.push(handler);
212
- return () => {
213
- this.closeHandlers = this.closeHandlers.filter(h => h !== handler);
214
- };
215
- }
216
- send(message) {
217
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
218
- throw new Error('WebSocket is not connected');
219
- }
220
- this.ws.send(JSON.stringify(message));
221
- }
222
- async stop(options = { delete: true }) {
223
- if (this.ws) {
224
- this.ws.close();
225
- }
226
- if (this.sandboxId) {
227
- const platformUrl = (this.options.platformUrl || process.env.CASTARI_PLATFORM_URL || DEFAULT_PLATFORM_URL).replace(/\/$/, '');
228
- try {
229
- const clientId = this.resolvedClientId || this.options.clientId || process.env.CASTARI_CLIENT_ID;
230
- const apiKey = this.resolvedPlatformApiKey || this.options.platformApiKey || process.env.CASTARI_API_KEY;
231
- const response = await fetch(`${platformUrl}/sandbox/stop`, {
232
- method: 'POST',
233
- headers: {
234
- 'Content-Type': 'application/json',
235
- ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
236
- },
237
- body: JSON.stringify({
238
- sandboxId: this.sandboxId,
239
- delete: options.delete,
240
- clientId
241
- })
242
- });
243
- if (!response.ok) {
244
- console.error(`Failed to stop sandbox: ${await response.text()}`);
245
- }
246
- else if (this.options.debug) {
247
- console.log(`🛑 Sandbox ${options.delete ? 'deleted' : 'stopped'}`);
248
- }
249
- }
250
- catch (err) {
251
- console.error('Failed to call stop endpoint:', err);
252
- }
90
+ /**
91
+ * Ensure the client is initialized before making requests
92
+ * @throws AuthenticationError if no credentials are available
93
+ */
94
+ async ensureAuthenticated() {
95
+ await this.initialize();
96
+ const auth = await getAuth();
97
+ if (!auth && !this.options.apiKey && !this.options.token) {
98
+ throw new AuthenticationError();
253
99
  }
254
100
  }
255
101
  }
102
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Credentials stored in ~/.castari/credentials.yaml
3
+ */
4
+ export interface Credentials {
5
+ token?: string;
6
+ api_key?: string;
7
+ }
8
+ /**
9
+ * Config stored in ~/.castari/config.yaml
10
+ */
11
+ export interface Config {
12
+ api_url?: string;
13
+ }
14
+ /**
15
+ * Load credentials from ~/.castari/credentials.yaml
16
+ */
17
+ export declare function loadCredentials(): Promise<Credentials>;
18
+ /**
19
+ * Save credentials to ~/.castari/credentials.yaml
20
+ * File is created with mode 0600 for security
21
+ */
22
+ export declare function saveCredentials(credentials: Credentials): Promise<void>;
23
+ /**
24
+ * Clear all stored credentials
25
+ */
26
+ export declare function clearCredentials(): Promise<void>;
27
+ /**
28
+ * Load config from ~/.castari/config.yaml
29
+ */
30
+ export declare function loadConfig(): Promise<Config>;
31
+ /**
32
+ * Save config to ~/.castari/config.yaml
33
+ */
34
+ export declare function saveConfig(config: Config): Promise<void>;
35
+ /**
36
+ * Get the API URL from config, env var, or default
37
+ */
38
+ export declare function getApiUrl(): Promise<string>;
39
+ /**
40
+ * Get the auth token or API key from env vars or config
41
+ * Priority: CASTARI_API_KEY env var > credentials file
42
+ */
43
+ export declare function getAuth(): Promise<{
44
+ type: 'api_key' | 'token';
45
+ value: string;
46
+ } | null>;
47
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAeA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAWD;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,WAAW,CAAC,CAO5D;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMtD;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAOlD;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI9D;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAcjD;AAED;;;GAGG;AACH,wBAAsB,OAAO,IAAI,OAAO,CAAC;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAiB5F"}
package/dist/config.js ADDED
@@ -0,0 +1,108 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { mkdir, readFile, writeFile, rm } from 'node:fs/promises';
4
+ import { existsSync } from 'node:fs';
5
+ import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
6
+ /** Directory for Castari config files */
7
+ const CONFIG_DIR = join(homedir(), '.castari');
8
+ /** Path to credentials file */
9
+ const CREDENTIALS_FILE = join(CONFIG_DIR, 'credentials.yaml');
10
+ /** Path to config file */
11
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.yaml');
12
+ /**
13
+ * Ensure the config directory exists
14
+ */
15
+ async function ensureConfigDir() {
16
+ if (!existsSync(CONFIG_DIR)) {
17
+ await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
18
+ }
19
+ }
20
+ /**
21
+ * Load credentials from ~/.castari/credentials.yaml
22
+ */
23
+ export async function loadCredentials() {
24
+ try {
25
+ const content = await readFile(CREDENTIALS_FILE, 'utf-8');
26
+ return parseYaml(content) || {};
27
+ }
28
+ catch {
29
+ return {};
30
+ }
31
+ }
32
+ /**
33
+ * Save credentials to ~/.castari/credentials.yaml
34
+ * File is created with mode 0600 for security
35
+ */
36
+ export async function saveCredentials(credentials) {
37
+ await ensureConfigDir();
38
+ const content = stringifyYaml(credentials);
39
+ await writeFile(CREDENTIALS_FILE, content, { mode: 0o600 });
40
+ }
41
+ /**
42
+ * Clear all stored credentials
43
+ */
44
+ export async function clearCredentials() {
45
+ try {
46
+ await rm(CREDENTIALS_FILE);
47
+ }
48
+ catch {
49
+ // File doesn't exist, that's fine
50
+ }
51
+ }
52
+ /**
53
+ * Load config from ~/.castari/config.yaml
54
+ */
55
+ export async function loadConfig() {
56
+ try {
57
+ const content = await readFile(CONFIG_FILE, 'utf-8');
58
+ return parseYaml(content) || {};
59
+ }
60
+ catch {
61
+ return {};
62
+ }
63
+ }
64
+ /**
65
+ * Save config to ~/.castari/config.yaml
66
+ */
67
+ export async function saveConfig(config) {
68
+ await ensureConfigDir();
69
+ const content = stringifyYaml(config);
70
+ await writeFile(CONFIG_FILE, content, { mode: 0o644 });
71
+ }
72
+ /**
73
+ * Get the API URL from config, env var, or default
74
+ */
75
+ export async function getApiUrl() {
76
+ // Check env var first
77
+ if (process.env.CASTARI_API_URL) {
78
+ return process.env.CASTARI_API_URL;
79
+ }
80
+ // Check config file
81
+ const config = await loadConfig();
82
+ if (config.api_url) {
83
+ return config.api_url;
84
+ }
85
+ // Default
86
+ return 'https://web-13239-04c55b73-wp4aqyqk.onporter.run';
87
+ }
88
+ /**
89
+ * Get the auth token or API key from env vars or config
90
+ * Priority: CASTARI_API_KEY env var > credentials file
91
+ */
92
+ export async function getAuth() {
93
+ // Check env var first (for CI/CD)
94
+ const apiKey = process.env.CASTARI_API_KEY;
95
+ if (apiKey) {
96
+ return { type: 'api_key', value: apiKey };
97
+ }
98
+ // Check credentials file
99
+ const credentials = await loadCredentials();
100
+ if (credentials.api_key) {
101
+ return { type: 'api_key', value: credentials.api_key };
102
+ }
103
+ if (credentials.token) {
104
+ return { type: 'token', value: credentials.token };
105
+ }
106
+ return null;
107
+ }
108
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}