langtrain 0.1.19 → 0.1.21

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/index.d.mts CHANGED
@@ -37,6 +37,9 @@ declare class AgentClient {
37
37
  create(agent: AgentCreate): Promise<Agent>;
38
38
  delete(agentId: string): Promise<void>;
39
39
  execute(agentId: string, input: any, messages?: any[], conversationId?: string): Promise<AgentRun>;
40
+ logs(agentId: string, limit?: number): Promise<{
41
+ logs: string[];
42
+ }>;
40
43
  }
41
44
  interface AgentConfig {
42
45
  system_prompt?: string;
@@ -183,4 +186,67 @@ declare namespace models {
183
186
  export { type models_Model as Model, models_ModelClient as ModelClient, type models_Permission as Permission };
184
187
  }
185
188
 
186
- export { type Agent, AgentClient, type AgentCreate, type AgentRun, agent as AgentTypes, type FeatureCheck, FileClient, type FileResponse, type FineTuneJobCreate, type FineTuneJobResponse, type Model, ModelClient, models as ModelTypes, SubscriptionClient, type SubscriptionInfo, TrainingClient };
189
+ interface Secret {
190
+ key: string;
191
+ created_at: string;
192
+ updated_at: string;
193
+ }
194
+ declare class SecretClient {
195
+ private config;
196
+ private client;
197
+ constructor(config: {
198
+ apiKey: string;
199
+ baseUrl?: string;
200
+ });
201
+ list(workspaceId?: string): Promise<Secret[]>;
202
+ set(key: string, value: string, workspaceId?: string): Promise<Secret>;
203
+ delete(key: string, workspaceId?: string): Promise<void>;
204
+ }
205
+
206
+ type secrets_Secret = Secret;
207
+ type secrets_SecretClient = SecretClient;
208
+ declare const secrets_SecretClient: typeof SecretClient;
209
+ declare namespace secrets {
210
+ export { type secrets_Secret as Secret, secrets_SecretClient as SecretClient };
211
+ }
212
+
213
+ interface GuardrailConfig {
214
+ pii_enabled: boolean;
215
+ pii_entities?: string[];
216
+ profanity_enabled: boolean;
217
+ profanity_threshold?: number;
218
+ blocked_topics?: string[];
219
+ regex_patterns?: string[];
220
+ min_length?: number;
221
+ max_length?: number;
222
+ }
223
+ interface Guardrail {
224
+ id: string;
225
+ workspace_id: string;
226
+ name: string;
227
+ description?: string;
228
+ config: GuardrailConfig;
229
+ is_active: boolean;
230
+ created_at: string;
231
+ updated_at: string;
232
+ }
233
+ interface GuardrailCreate {
234
+ name: string;
235
+ description?: string;
236
+ config: GuardrailConfig;
237
+ }
238
+ declare class GuardrailClient {
239
+ private config;
240
+ private client;
241
+ constructor(config: {
242
+ apiKey: string;
243
+ baseUrl?: string;
244
+ });
245
+ list(workspaceId?: string): Promise<Guardrail[]>;
246
+ get(guardrailId: string): Promise<Guardrail>;
247
+ create(data: GuardrailCreate): Promise<Guardrail>;
248
+ delete(guardrailId: string): Promise<void>;
249
+ apply(datasetId: string, guardrailId: string): Promise<any>;
250
+ }
251
+
252
+ export { type Agent, AgentClient, type AgentCreate, type AgentRun, agent as AgentTypes, type FeatureCheck, FileClient, type FileResponse, type FineTuneJobCreate, type FineTuneJobResponse, type Guardrail, GuardrailClient, type GuardrailConfig, type GuardrailCreate, type Model, ModelClient, models as ModelTypes, type Secret, SecretClient, secrets as SecretTypes, SubscriptionClient, type SubscriptionInfo, TrainingClient };
package/dist/index.d.ts CHANGED
@@ -37,6 +37,9 @@ declare class AgentClient {
37
37
  create(agent: AgentCreate): Promise<Agent>;
38
38
  delete(agentId: string): Promise<void>;
39
39
  execute(agentId: string, input: any, messages?: any[], conversationId?: string): Promise<AgentRun>;
40
+ logs(agentId: string, limit?: number): Promise<{
41
+ logs: string[];
42
+ }>;
40
43
  }
41
44
  interface AgentConfig {
42
45
  system_prompt?: string;
@@ -183,4 +186,67 @@ declare namespace models {
183
186
  export { type models_Model as Model, models_ModelClient as ModelClient, type models_Permission as Permission };
184
187
  }
185
188
 
186
- export { type Agent, AgentClient, type AgentCreate, type AgentRun, agent as AgentTypes, type FeatureCheck, FileClient, type FileResponse, type FineTuneJobCreate, type FineTuneJobResponse, type Model, ModelClient, models as ModelTypes, SubscriptionClient, type SubscriptionInfo, TrainingClient };
189
+ interface Secret {
190
+ key: string;
191
+ created_at: string;
192
+ updated_at: string;
193
+ }
194
+ declare class SecretClient {
195
+ private config;
196
+ private client;
197
+ constructor(config: {
198
+ apiKey: string;
199
+ baseUrl?: string;
200
+ });
201
+ list(workspaceId?: string): Promise<Secret[]>;
202
+ set(key: string, value: string, workspaceId?: string): Promise<Secret>;
203
+ delete(key: string, workspaceId?: string): Promise<void>;
204
+ }
205
+
206
+ type secrets_Secret = Secret;
207
+ type secrets_SecretClient = SecretClient;
208
+ declare const secrets_SecretClient: typeof SecretClient;
209
+ declare namespace secrets {
210
+ export { type secrets_Secret as Secret, secrets_SecretClient as SecretClient };
211
+ }
212
+
213
+ interface GuardrailConfig {
214
+ pii_enabled: boolean;
215
+ pii_entities?: string[];
216
+ profanity_enabled: boolean;
217
+ profanity_threshold?: number;
218
+ blocked_topics?: string[];
219
+ regex_patterns?: string[];
220
+ min_length?: number;
221
+ max_length?: number;
222
+ }
223
+ interface Guardrail {
224
+ id: string;
225
+ workspace_id: string;
226
+ name: string;
227
+ description?: string;
228
+ config: GuardrailConfig;
229
+ is_active: boolean;
230
+ created_at: string;
231
+ updated_at: string;
232
+ }
233
+ interface GuardrailCreate {
234
+ name: string;
235
+ description?: string;
236
+ config: GuardrailConfig;
237
+ }
238
+ declare class GuardrailClient {
239
+ private config;
240
+ private client;
241
+ constructor(config: {
242
+ apiKey: string;
243
+ baseUrl?: string;
244
+ });
245
+ list(workspaceId?: string): Promise<Guardrail[]>;
246
+ get(guardrailId: string): Promise<Guardrail>;
247
+ create(data: GuardrailCreate): Promise<Guardrail>;
248
+ delete(guardrailId: string): Promise<void>;
249
+ apply(datasetId: string, guardrailId: string): Promise<any>;
250
+ }
251
+
252
+ export { type Agent, AgentClient, type AgentCreate, type AgentRun, agent as AgentTypes, type FeatureCheck, FileClient, type FileResponse, type FineTuneJobCreate, type FineTuneJobResponse, type Guardrail, GuardrailClient, type GuardrailConfig, type GuardrailCreate, type Model, ModelClient, models as ModelTypes, type Secret, SecretClient, secrets as SecretTypes, SubscriptionClient, type SubscriptionInfo, TrainingClient };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var chunkPAHGEWDE_js=require('./chunk-PAHGEWDE.js');Object.defineProperty(exports,"AgentClient",{enumerable:true,get:function(){return chunkPAHGEWDE_js.e}});Object.defineProperty(exports,"AgentTypes",{enumerable:true,get:function(){return chunkPAHGEWDE_js.f}});Object.defineProperty(exports,"FileClient",{enumerable:true,get:function(){return chunkPAHGEWDE_js.g}});Object.defineProperty(exports,"Langtune",{enumerable:true,get:function(){return chunkPAHGEWDE_js.c}});Object.defineProperty(exports,"Langvision",{enumerable:true,get:function(){return chunkPAHGEWDE_js.a}});Object.defineProperty(exports,"ModelClient",{enumerable:true,get:function(){return chunkPAHGEWDE_js.j}});Object.defineProperty(exports,"ModelTypes",{enumerable:true,get:function(){return chunkPAHGEWDE_js.k}});Object.defineProperty(exports,"SubscriptionClient",{enumerable:true,get:function(){return chunkPAHGEWDE_js.i}});Object.defineProperty(exports,"Text",{enumerable:true,get:function(){return chunkPAHGEWDE_js.d}});Object.defineProperty(exports,"TrainingClient",{enumerable:true,get:function(){return chunkPAHGEWDE_js.h}});Object.defineProperty(exports,"Vision",{enumerable:true,get:function(){return chunkPAHGEWDE_js.b}});//# sourceMappingURL=index.js.map
1
+ 'use strict';var chunkQZ6U7AJN_js=require('./chunk-QZ6U7AJN.js');chunkQZ6U7AJN_js.r();Object.defineProperty(exports,"AgentClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.h}});Object.defineProperty(exports,"AgentTypes",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.i}});Object.defineProperty(exports,"FileClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.j}});Object.defineProperty(exports,"GuardrailClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.q}});Object.defineProperty(exports,"Langtune",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.f}});Object.defineProperty(exports,"Langvision",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.d}});Object.defineProperty(exports,"ModelClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.m}});Object.defineProperty(exports,"ModelTypes",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.n}});Object.defineProperty(exports,"SecretClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.o}});Object.defineProperty(exports,"SecretTypes",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.p}});Object.defineProperty(exports,"SubscriptionClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.l}});Object.defineProperty(exports,"Text",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.g}});Object.defineProperty(exports,"TrainingClient",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.k}});Object.defineProperty(exports,"Vision",{enumerable:true,get:function(){return chunkQZ6U7AJN_js.e}});//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export{e as AgentClient,f as AgentTypes,g as FileClient,c as Langtune,a as Langvision,j as ModelClient,k as ModelTypes,i as SubscriptionClient,d as Text,h as TrainingClient,b as Vision}from'./chunk-Q46V6ODQ.mjs';//# sourceMappingURL=index.mjs.map
1
+ import {r}from'./chunk-TDQXC2RA.mjs';export{h as AgentClient,i as AgentTypes,j as FileClient,q as GuardrailClient,f as Langtune,d as Langvision,m as ModelClient,n as ModelTypes,o as SecretClient,p as SecretTypes,l as SubscriptionClient,g as Text,k as TrainingClient,e as Vision}from'./chunk-TDQXC2RA.mjs';r();//# sourceMappingURL=index.mjs.map
2
2
  //# sourceMappingURL=index.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langtrain",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Unified JavaScript SDK for Langtrain Ecosystem",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -41,6 +41,7 @@
41
41
  "dependencies": {
42
42
  "@clack/prompts": "^1.0.1",
43
43
  "axios": "^1.13.5",
44
+ "cli-table3": "^0.6.5",
44
45
  "commander": "^14.0.3",
45
46
  "gradient-string": "^3.0.0",
46
47
  "kleur": "^4.1.5",
package/src/cli/auth.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { password, isCancel, cancel, intro, green, yellow, red, bgCyan, black, spinner, gray } from './ui';
1
+ import { password, isCancel, cancel, intro, green, yellow, red, bgMagenta, black, spinner, gray } from './ui';
2
2
  import { getConfig, saveConfig } from './config';
3
3
  import { SubscriptionClient, SubscriptionInfo } from '../index';
4
4
 
@@ -58,7 +58,7 @@ export async function getSubscription(apiKey: string): Promise<SubscriptionInfo
58
58
  const info = await client.getStatus();
59
59
 
60
60
  // Enhance: Show plan details immediately on auth check
61
- const planLabel = info.plan === 'pro' ? bgCyan(' PRO ') : info.plan.toUpperCase();
61
+ const planLabel = info.plan === 'pro' ? bgMagenta(' PRO ') : info.plan.toUpperCase();
62
62
  s.stop(green(`Authenticated as ${planLabel}`));
63
63
 
64
64
  if (info.is_active === false) {
@@ -0,0 +1,20 @@
1
+ import Table from 'cli-table3';
2
+ import { colors } from '../ui';
3
+
4
+ export function createTable(headers: string[]) {
5
+ return new Table({
6
+ head: headers.map(h => colors.magenta(colors.bold(h))),
7
+ chars: {
8
+ 'top': '─', 'top-mid': '┬', 'top-left': '┌', 'top-right': '┐',
9
+ 'bottom': '─', 'bottom-mid': '┴', 'bottom-left': '└', 'bottom-right': '┘',
10
+ 'left': '│', 'left-mid': '├', 'mid': '─', 'mid-mid': '┼',
11
+ 'right': '│', 'right-mid': '┤', 'middle': '│'
12
+ },
13
+ style: {
14
+ 'padding-left': 1,
15
+ 'padding-right': 1,
16
+ head: [], // handle colors manually
17
+ border: ['gray']
18
+ }
19
+ });
20
+ }
@@ -1,4 +1,4 @@
1
- import { text, select, confirm, isCancel, cancel, spinner, intro, red, green, yellow, gray, bgCyan, black, gradient } from '../ui';
1
+ import { text, select, confirm, isCancel, cancel, spinner, intro, red, green, yellow, gray, bgMagenta, black, gradient, createTable } from '../ui';
2
2
  import { AgentClient, ModelClient } from '../../index';
3
3
 
4
4
  export async function handleAgentCreate(client: AgentClient, modelClient: ModelClient) {
@@ -136,6 +136,19 @@ export async function handleAgentList(client: AgentClient) {
136
136
  return;
137
137
  }
138
138
 
139
+ // Display Table
140
+ const table = createTable(['ID', 'Name', 'Model', 'Created']);
141
+ agents.forEach(a => {
142
+ table.push([
143
+ a.id.substring(0, 8) + '...',
144
+ a.name,
145
+ (a.config as any)?.model || 'default',
146
+ new Date(a.created_at).toLocaleDateString()
147
+ ]);
148
+ });
149
+ console.log(table.toString());
150
+ console.log(''); // spacer
151
+
139
152
  const agentId = await select({
140
153
  message: 'Select an agent to run:',
141
154
  options: agents.map(a => ({ value: a.id, label: a.name, hint: a.description || 'No description' }))
@@ -146,12 +159,31 @@ export async function handleAgentList(client: AgentClient) {
146
159
  await handleAgentRun(client, agentId as string, agents.find(a => a.id === agentId)?.name || 'Agent');
147
160
  }
148
161
 
149
- export async function handleAgentRun(client: AgentClient, agentId: string, agentName: string) {
150
- intro(bgCyan(black(` Chatting with ${agentName} `)));
162
+ export async function handleAgentRun(client: AgentClient, agentId: string, agentName: string, initialMessage?: string) {
163
+ intro(bgMagenta(black(` Chatting with ${agentName} `)));
151
164
  console.log(gray('Type "exit" to quit conversation.'));
152
165
 
153
166
  let conversationId: string | undefined = undefined;
154
167
 
168
+ // Send initial message if provided
169
+ if (initialMessage) {
170
+ const s = spinner();
171
+ s.start('Agent is analyzing...');
172
+ try {
173
+ const result = await client.execute(agentId, { prompt: initialMessage }, [], conversationId);
174
+ s.stop();
175
+ if ((result.output as any)?.response) {
176
+ console.log(gradient.pastel(`Agent: ${(result.output as any).response}`));
177
+ } else {
178
+ console.log(gradient.pastel(`Agent: ${JSON.stringify(result.output)}`));
179
+ }
180
+ conversationId = result.conversation_id;
181
+ } catch (e: any) {
182
+ s.stop(red('Error running agent.'));
183
+ console.error(e);
184
+ }
185
+ }
186
+
155
187
  while (true) {
156
188
  const input = await text({
157
189
  message: 'You:',
@@ -1,7 +1,7 @@
1
1
  import { text, select, confirm, isCancel, cancel, spinner, intro, red, green, yellow, gray } from '../ui';
2
2
  import { getConfig } from '../config';
3
- import { FileClient } from '../../index';
4
- import path from 'path';
3
+ import { FileClient, AgentClient, GuardrailClient } from '../../index';
4
+ import { handleAgentRun } from './agent';
5
5
  import fs from 'fs';
6
6
 
7
7
  export async function handleDataUpload(client: FileClient) {
@@ -73,12 +73,135 @@ export async function handleDataList(client: FileClient) {
73
73
  return;
74
74
  }
75
75
 
76
- // Just list them for now, maybe select to delete later?
77
- files.forEach(f => {
78
- console.log(`${f.id.padEnd(30)} ${f.filename.padEnd(20)} ${f.purpose} (${f.bytes}b)`);
76
+ const file = await select({
77
+ message: 'Select file to analyze (or cancel to exit):',
78
+ options: files.map(f => ({ value: f.id, label: `${f.filename} (${formatBytes(f.bytes)})` }))
79
79
  });
80
80
 
81
+ if (isCancel(file)) return;
82
+
83
+ await handleDataAnalyze(client, file as string);
84
+
81
85
  } catch (e: any) {
82
86
  s.stop(red(`Failed to list files: ${e.message}`));
83
87
  }
84
88
  }
89
+
90
+ export async function handleDataAnalyze(client: FileClient, fileId: string) {
91
+ const config = getConfig();
92
+ const s2 = spinner();
93
+ s2.start('Connecting to Data Analyst...');
94
+
95
+ try {
96
+ const agentClient = new AgentClient({ apiKey: config.apiKey || '', baseUrl: config.baseUrl });
97
+ const agents = await agentClient.list();
98
+ // Check name (name is optional in type but we know it exists for system agent)
99
+ let analyst = agents.find(a => a.name && a.name === "Langtrain Data Analyst");
100
+
101
+ if (!analyst) {
102
+ s2.stop(yellow('Data Analyst agent (System) not found. Please contact admin to provision it.'));
103
+ return;
104
+ }
105
+
106
+ s2.stop(green('Connected to Data Analyst.'));
107
+
108
+ console.log(gray(`\nAnalyzing dataset ${fileId}...\n`));
109
+
110
+ await handleAgentRun(agentClient, analyst.id, analyst.name, `Please analyze the dataset with ID: ${fileId}`);
111
+
112
+ } catch (e: any) {
113
+ s2.stop(red(`Failed to connect: ${e.message}`));
114
+ }
115
+ }
116
+
117
+ export async function handleDataRefine(client: FileClient, fileId?: string) {
118
+ const config = getConfig();
119
+ const gClient = new GuardrailClient({ apiKey: config.apiKey || '', baseUrl: config.baseUrl });
120
+
121
+ // 0. Select File if not provided
122
+ if (!fileId) {
123
+ const s = spinner();
124
+ s.start('Fetching files...');
125
+ try {
126
+ // Need workspace ID logic similar to List?
127
+ // client.list takes workspaceId.
128
+ const wId = config.workspace_id || '';
129
+ const files = await client.list(wId);
130
+ s.stop(`Found ${files.length} files`);
131
+
132
+ if (files.length === 0) {
133
+ console.log(yellow('No files found. Upload one first.'));
134
+ return;
135
+ }
136
+
137
+ const selection = await select({
138
+ message: 'Select file to refine:',
139
+ options: files.map(f => ({ value: f.id, label: `${f.filename} (${formatBytes(f.bytes)})` }))
140
+ });
141
+
142
+ if (isCancel(selection)) return;
143
+ fileId = selection as string;
144
+
145
+ } catch (e: any) {
146
+ s.stop(red(`Failed to fetch files: ${e.message}`));
147
+ return;
148
+ }
149
+ }
150
+
151
+ // 1. Select Guardrail
152
+ const s = spinner();
153
+ s.start('Fetching guardrails...');
154
+ let guardId = "";
155
+ try {
156
+ const guards = await gClient.list();
157
+ s.stop(`Found ${guards.length} guardrails`);
158
+
159
+ if (guards.length === 0) {
160
+ console.log(yellow('No guardrails found. Please create one first using "lt guardrails create".'));
161
+ return;
162
+ }
163
+
164
+ const selection = await select({
165
+ message: 'Select a Guardrail to apply:',
166
+ options: guards.map((g: any) => ({
167
+ value: g.id,
168
+ label: g.name,
169
+ hint: g.description
170
+ }))
171
+ });
172
+
173
+ if (isCancel(selection)) return;
174
+ guardId = selection as string;
175
+
176
+ } catch (e: any) {
177
+ s.stop(red(`Failed to fetch guardrails: ${e.message}`));
178
+ return;
179
+ }
180
+
181
+ // 2. Apply
182
+ const s2 = spinner();
183
+ s2.start('Applying guardrail (filtering dataset)...');
184
+
185
+ try {
186
+ const result = await gClient.apply(fileId, guardId);
187
+ s2.stop(green('Dataset refined successfully!'));
188
+
189
+ console.log(gray('Stats:'));
190
+ console.log(`Original Rows: ${result.original_rows}`);
191
+ console.log(`Filtered Rows: ${result.filtered_rows}`);
192
+ console.log(red(`Removed: ${result.removed_rows} rows`));
193
+ console.log(green(`New Dataset ID: ${result.new_dataset_id}`));
194
+
195
+ } catch (e: any) {
196
+ s2.stop(red(`Failed to refine dataset: ${e.message}`));
197
+ }
198
+ }
199
+
200
+ function formatBytes(bytes: number, decimals = 2) {
201
+ if (!+bytes) return '0 Bytes';
202
+ const k = 1024;
203
+ const dm = decimals < 0 ? 0 : decimals;
204
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
205
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
206
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
207
+ }
@@ -0,0 +1,89 @@
1
+ import { intro, outro, spinner, green, red, yellow, showInfo, gray, showSuccess } from '../ui';
2
+ import { SecretClient } from '../../index';
3
+ import { text, confirm, select, isCancel, cancel } from '../ui';
4
+ import { getConfig } from '../config';
5
+
6
+ export async function handleEnvList(client: SecretClient) {
7
+ const s = spinner();
8
+ s.start('Fetching secrets...');
9
+ const config = getConfig();
10
+ try {
11
+ const secrets = await client.list(config.workspace_id);
12
+ s.stop(`Found ${secrets.length} secrets`);
13
+
14
+ if (secrets.length === 0) {
15
+ console.log(gray('No secrets found. Use "lt env set" to add one.'));
16
+ return;
17
+ }
18
+
19
+ console.log(gray('------------------------------------------------'));
20
+ secrets.forEach(sec => {
21
+ console.log(`${sec.key.padEnd(30)} ${gray('******')}`);
22
+ });
23
+ console.log(gray('------------------------------------------------'));
24
+
25
+ } catch (e: any) {
26
+ s.stop(red(`Failed to list secrets: ${e.message}`));
27
+ }
28
+ }
29
+
30
+ export async function handleEnvSet(client: SecretClient, keyVal?: string) {
31
+ let key = '';
32
+ let value = '';
33
+
34
+ if (keyVal && keyVal.includes('=')) {
35
+ const parts = keyVal.split('=');
36
+ key = parts[0];
37
+ value = parts.slice(1).join('=');
38
+ } else {
39
+ key = await text({ message: 'Secret Key:', placeholder: 'OPENAI_API_KEY' }) as string;
40
+ if (isCancel(key)) return;
41
+
42
+ value = await text({ message: 'Secret Value:', placeholder: 'sk-...' }) as string;
43
+ if (isCancel(value)) return;
44
+ }
45
+
46
+ const s = spinner();
47
+ s.start(`Setting ${key}...`);
48
+ const config = getConfig();
49
+
50
+ try {
51
+ await client.set(key, value, config.workspace_id);
52
+ s.stop(green(`Secret ${key} set successfully.`));
53
+ } catch (e: any) {
54
+ s.stop(red(`Failed to set secret: ${e.message}`));
55
+ }
56
+ }
57
+
58
+ // Interactive menu for env
59
+ export async function handleEnvMenu(client: SecretClient) {
60
+ const action = await select({
61
+ message: 'Manage Secrets',
62
+ options: [
63
+ { value: 'list', label: 'List Secrets' },
64
+ { value: 'set', label: 'Set Secret' },
65
+ { value: 'remove', label: 'Remove Secret' },
66
+ { value: 'back', label: 'Back' }
67
+ ]
68
+ });
69
+
70
+ if (isCancel(action) || action === 'back') return;
71
+
72
+ if (action === 'list') await handleEnvList(client);
73
+ if (action === 'set') await handleEnvSet(client);
74
+ if (action === 'remove') {
75
+ const key = await text({ message: 'Key to remove:' });
76
+ if (!isCancel(key)) {
77
+ const s = spinner();
78
+ s.start('Removing...');
79
+ try {
80
+ // need workspace_id
81
+ const config = getConfig();
82
+ await client.delete(key as string, config.workspace_id);
83
+ s.stop(green('Removed.'));
84
+ } catch (e: any) {
85
+ s.stop(red(`Failed: ${e.message}`));
86
+ }
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,100 @@
1
+ import { text, select, confirm, isCancel, cancel, spinner, intro, red, green, yellow, gray } from '../ui';
2
+ import { getConfig } from '../config';
3
+ import { GuardrailClient } from '../../index';
4
+
5
+ export async function handleGuardrailList(client: any) { // using any to match index signature, but we instantiate specific client inside
6
+ const config = getConfig();
7
+ const gClient = new GuardrailClient({ apiKey: config.apiKey || '', baseUrl: config.baseUrl });
8
+
9
+ const s = spinner();
10
+ s.start('Fetching guardrails...');
11
+
12
+ try {
13
+ const guards = await gClient.list();
14
+ s.stop(`Found ${guards.length} guardrails`);
15
+
16
+ if (guards.length === 0) {
17
+ console.log(yellow('No guardrails found. Create one with "lt guardrails create".'));
18
+ return;
19
+ }
20
+
21
+ guards.forEach((g: any) => {
22
+ console.log(green(`• ${g.name}`) + gray(` (ID: ${g.id})`));
23
+ if (g.description) console.log(gray(` ${g.description}`));
24
+ console.log(gray(` Config: PII=${g.config.pii_enabled}, MinLen=${g.config.min_length}`));
25
+ console.log('');
26
+ });
27
+
28
+ } catch (e: any) {
29
+ s.stop(red(`Failed to list guardrails: ${e.message}`));
30
+ }
31
+ }
32
+
33
+ export async function handleGuardrailCreate(client: any) {
34
+ const config = getConfig();
35
+ const gClient = new GuardrailClient({ apiKey: config.apiKey || '', baseUrl: config.baseUrl });
36
+
37
+ intro('Create a new Data Guardrail');
38
+
39
+ const name = await text({
40
+ message: 'Guardrail Name:',
41
+ placeholder: 'e.g. Strict Safety Policy',
42
+ validate(value) {
43
+ if (!value) return 'Name is required';
44
+ }
45
+ });
46
+ if (isCancel(name)) return;
47
+
48
+ const description = await text({
49
+ message: 'Description (optional):',
50
+ placeholder: 'Filters PII and short text',
51
+ });
52
+ if (isCancel(description)) return;
53
+
54
+ // Interactive Config
55
+ const minLen = await text({
56
+ message: 'Minimum Text Length (0 for no limit):',
57
+ initialValue: '0',
58
+ validate(value) {
59
+ if (isNaN(Number(value))) return 'Must be a number';
60
+ }
61
+ });
62
+ if (isCancel(minLen)) return;
63
+
64
+ const enablePii = await confirm({
65
+ message: 'Enable PII Filtering (Email/Phone)?',
66
+ initialValue: false
67
+ });
68
+ if (isCancel(enablePii)) return;
69
+
70
+ const patterns = await text({
71
+ message: 'Regex Patterns to Block (comma separated, optional):',
72
+ placeholder: 'e.g. bad_word, another_one',
73
+ });
74
+ if (isCancel(patterns)) return;
75
+
76
+ const s = spinner();
77
+ s.start('Creating guardrail...');
78
+
79
+ try {
80
+ const regexList = (patterns as string).split(',').map(p => p.trim()).filter(p => p.length > 0);
81
+
82
+ const payload = {
83
+ name,
84
+ description,
85
+ config: {
86
+ min_length: Number(minLen),
87
+ pii_enabled: enablePii,
88
+ regex_patterns: regexList,
89
+ profanity_enabled: false
90
+ }
91
+ };
92
+
93
+ const result = await gClient.create(payload);
94
+ s.stop(green(`Guardrail "${result.name}" created successfully!`));
95
+ console.log(gray(`ID: ${result.id}`));
96
+
97
+ } catch (e: any) {
98
+ s.stop(red(`Failed to create guardrail: ${e.message}`));
99
+ }
100
+ }